Как записать подробный вывод Powershell CmdLet, когда CmdLet вызывается программно из C #

ЗАДНИЙ ПЛАН

  • Я использую Powershell 2.0 в Windows 7.
  • Я пишу командлет в модуле Powershell («модуль» впервые появился в Powershell 2.0).
  • Чтобы протестировать командлет, я пишу модульные тесты в Visual Studio 2008, которые программно вызывают командлет.

ССЫЛКА

  • В этой статье на MSDN под названием «Как вызвать командлет из командлета» показано, как вызвать командлет из C #.

ИСХОДНЫЙ КОД

  • Это дистиллированная версия моего фактического кода, который я сделал как можно меньше, чтобы вы могли ясно увидеть проблему, с которой я столкнулся:

    using System;
    using System.Management.Automation;   
    
    namespace DemoCmdLet1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var cmd = new GetColorsCommand();
    
                    foreach ( var i in cmd.Invoke<string>())
                    {
                       Console.WriteLine("- " + i );   
                    } 
               } 
           } 
    
        [Cmdlet("Get", "Colors")]
        public class GetColorsCommand : Cmdlet
        {
            protected override void ProcessRecord()
            {
                this.WriteObject("Hello");
                this.WriteVerbose("World");
            }
    
        }
    }
    

КОММЕНТАРИИ

  • Я понимаю, как включить и записать подробный вывод из командной строки Powershell; это не проблема.
  • В этом случае я программно вызываю командлет из C #.
  • Ничего из того, что я нашел, не соответствует моему конкретному сценарию. В некоторых статьях предлагается реализовать собственный PSHost, но это кажется дорогостоящим, а также кажется, что командлет нужно вызывать как текст, чего я бы хотел избежать, потому что он не так строго типизирован.

ОБНОВЛЕНИЕ 2009-07-20

Вот исходный код, основанный на ответе ниже.

Некоторые вещи мне до сих пор не ясны: * Как вызвать командлет "Get-Colors" (в идеале без необходимости передавать его в виде строки в объект ps) * Как получить подробный вывод по мере его создания < / em> вместо того, чтобы получить их коллекцию в конце.

    using System;
    using System.Management.Automation;   

    namespace DemoCmdLet1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var ps = System.Management.Automation.PowerShell.Create();

                ps.Commands.AddScript("$verbosepreference='continue'; write-verbose 42");

                foreach ( var i in ps.Invoke<string>())
                {
                   Console.WriteLine("normal output: {0}" , i );   
                }
                foreach (var i in ps.Streams.Verbose)
                {
                    Console.WriteLine("verbose output: {0}" , i);
                }

            }
        }

        [Cmdlet("Get", "Colors")]
        public class GetColorsCommand : Cmdlet
        {
            protected override void ProcessRecord()
            {
                this.WriteObject("Red");
                this.WriteVerbose("r");
                this.WriteObject("Green");
                this.WriteVerbose("g");
                this.WriteObject("Blue");
                this.WriteVerbose("b");

            }

        }
    }

Приведенный выше код генерирует такой вывод:

d:\DemoCmdLet1\DemoCmdLet1>bin\Debug\DemoCmdLet1.exe
verbose output: 42

ОБНОВЛЕНИЕ 16 января 2010 г.

с помощью класса Powershell (который находится в System.Management.Automation, но только в той версии сборки, которая поставляется с SDK powershell 2.0, а не в Windows 7), я могу программно вызвать командлет и получить подробный вывод. Оставшаяся часть - это фактически добавить настраиваемый командлет к этому экземпляру PowerShell - потому что это было моей первоначальной целью - провести модульное тестирование моих командлетов, а не тех, которые поставляются с PowerShell.

class Program
{
    static void Main(string[] args)
    {
        var ps = System.Management.Automation.PowerShell.Create();
        ps.AddCommand("Get-Process");
        ps.AddParameter("Verbose");
        ps.Streams.Verbose.DataAdded += Verbose_DataAdded;
        foreach (PSObject result in ps.Invoke())
        {
            Console.WriteLine(
                    "output: {0,-24}{1}",
                    result.Members["ProcessName"].Value,
                    result.Members["Id"].Value);
        } 
        Console.ReadKey();
    }

    static void Verbose_DataAdded(object sender, DataAddedEventArgs e)
    {
        Console.WriteLine( "verbose output: {0}", e.Index);
    }
}


[Cmdlet("Get", "Colors")]
public class GetColorsCommand : Cmdlet
{
    protected override void ProcessRecord()
    {
        this.WriteObject("Hello");
        this.WriteVerbose("World");
    }
}

person namenlos    schedule 18.07.2009    source источник
comment
в моем ответе отсутствовала обратная кавычка `- это предотвращает преждевременную оценку переменной $ verbosepreference.   -  person x0n    schedule 20.07.2009
comment
Почему вы не отметили мой ответ как ответ?   -  person x0n    schedule 12.01.2010
comment
Если вам нужны подробные записи в реальном времени, подпишитесь на событие datachanged в свойстве streams экземпляра PowerShell.   -  person x0n    schedule 12.01.2010
comment
Я поднял вопрос, чтобы учесть новую информацию, которую вы предоставили. Я все еще не могу сказать, что на этот вопрос дан ответ, поскольку я не понял, как добавить командлет Get-Colors в экземпляр Powershell.   -  person namenlos    schedule 17.01.2010
comment
Вам, наверное, стоило задать новый вопрос. Когда я ответил на ваш вопрос, вы добавили новый вопрос к старому.   -  person x0n    schedule 11.01.2011


Ответы (2)


  • Подробный вывод фактически не выводится, если $VerbosePreference не установлен по крайней мере на «Продолжить».
  • Используйте тип PowerShell для запуска cmdlet и считайте VerboseRecord экземпляры из Streams.Verbose свойства

Пример в сценарии PowerShell:

ps> $ps = [powershell]::create()
ps> $ps.Commands.AddScript("`$verbosepreference='continue'; write-verbose 42")
ps> $ps.invoke()
ps> $ps.streams.verbose
Message   InvocationInfo                          PipelineIterationInfo
-------   --------------                          ---------------------
42        System.Management.Automation.Invocat... {0, 0}

Это должно быть легко перевести на C #.

person x0n    schedule 18.07.2009

1.        string scriptFile = "Test.ps1";
2.        using (PowerShell ps = PowerShell.Create())
3.        {
4.          const string getverbose = "$verbosepreference='continue'"; 
5.          ps.AddScript(string.Format(getverbose));
6.          ps.Invoke();
7.          ps.Commands.Clear();
8.          ps.AddScript(@".\" + scriptFile);
9.          ps.Invoke();
10.         foreach (var v in ps.Streams.Verbose)
11.         {
12.               Console.WriteLine(v.Message);
13.         }
14.       }

Важными строками являются строки 5 и 6. Это в основном устанавливает $ verbosepreference для сеанса и для предстоящих новых команд и сценариев.

person Varun    schedule 25.03.2014