如何从C#以编程方式调用CmdLet时捕获Powershell CmdLet的详细输出

背景

  • 我在Windows 7上使用Powershell 2.0。
  • 我在Powershell模块中编写了一个cmdlet(“模块”是Powershell 2.0的新增function)。
  • 为了测试cmdlet,我在Visual Studio 2008中编写unit testing,以编程方式调用cmdlet。

参考

  • MSDN上的这篇文章名为“如何从Cmdlet中调用Cmdlet”,展示了如何从C#调用cmdlet。

来源代码

  • 这是我的实际代码的简化版本 – 我已尽可能小,以便您可以清楚地看到我遇到的问题:

    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()) { Console.WriteLine("- " + i ); } } } [Cmdlet("Get", "Colors")] public class GetColorsCommand : Cmdlet { protected override void ProcessRecord() { this.WriteObject("Hello"); this.WriteVerbose("World"); } } } 

评论

  • 我了解如何从Powershell命令行启用和捕获详细输出; 那不是问题。
  • 在这种情况下,我以编程方式从C#调用cmdlet。
  • 我找到的任何内容都没有解决我的具体情况。 一些文章建议我应该实现我自己的PSHost,但看起来很昂贵,而且似乎必须将cmdlet称为文本,我想避免,因为它不是那么强类型。

更新于2009-07-20

以下是基于以下答案的源代码。

有些事情对我来说仍然不清楚:*如何调用“Get-Colors”cmdlet(理想情况下不必将其作为字符串传递给ps对象)*如何获取生成的详细输出而不是获取最后收集它们。

  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()) { 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 

更新2010-01-16

通过使用Powershell类(在System.Management.Automation中找到,但仅在powershell 2.0 SDK附带的程序集版本中,而不是Windows 7上开箱即用的版本),我可以通过编程方式调用cmdlet和得到详细的输出。 剩下的部分是实际向该PowerShell实例添加一个自定义cmdlet – 因为这是我的最初目标 – unit testing我的cmdlet而不是PowerShell附带的cmdlet。

 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"); } } 

  • 除非$VerbosePreference至少设置为“Continue”,否则实际上不会输出详细输出。
  • 使用PowerShell类型运行cmdlet ,并从Streams.Verbose读取VerboseRecord实例

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#。

 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。