如何在 C# 中从 powershell class 捕获完整的调用文本

How to catch full invoke text from powershell class in C#

我想在 PowerShell 中执行 运行 命令时准确捕获输出。

例如,当我输入 LS 时,我得到:

然而当我使用这段代码时:

        Runspace runspace = RunspaceFactory.CreateRunspace();
        runspace.Open();

        PowerShell ps = PowerShell.Create(); // Create a new PowerShell instance
        ps.Runspace = runspace; // Add the instance to the runspace
        ps.Commands.AddScript("ls"); // Add a script

        Collection<PSObject> results = ps.Invoke();

        runspace.Close();


        StringBuilder stringBuilder = new StringBuilder();
        foreach (PSObject obj in results)
        {
            Console.WriteLine(obj.ToString());
        }

我得到以下输出:

Microsoft.Management.Infrastructure.dll
System.Management.Automation.dll
System.Management.Automation.xml
WpfApp1.exe
WpfApp1.exe.config
WpfApp1.pdb

尽管此输出可能会派上用场,但在其他应用程序中我无法在 return 中获得正确的输出,因此我更喜欢在 PowerShell 本身中看到的准确输出。

有没有办法逐行读取我在 PowerShell 中得到的输出?

您可以使用此命令从 powershell 获得压缩的 json 输出

ls | ConvertTo-Json -Compress

然后反序列化。此命令还提供比在 powershell 输出中看到的更多信息。

如果您想要 powershell 生成的确切文本,那么您可以在 powershell 命令中使用 Out-String

ps.Commands.AddScript("ls | Out-String");


您还可以通过访问 PSObjectProperties 来读取值:

foreach (PSObject obj in results)
{
    var name = obj.Properties["Name"]?.Value.ToString()
    var mode = obj.Properties["Mode"]?.Value.ToString();
    var length = obj.Properties["Length"]?.Value.ToString();
    var lastMod = (DateTime?)obj.Properties["LastWriteTime"]?.Value;

    Console.WriteLine(string.Format("{0} {1} {2} {3}", mode, lastMod, length, name));
}

注意,正如的回答中提到的,你不需要使用Runspace来执行这个powershell .考虑使用 Get-ChildItem 而不是 ls.

注意:此答案还推荐 显示的内容,以更集中的方式和补充信息。

修改您的脚本以将您的命令传送到 Out-String cmdlet,它使用 PowerShell 的格式化系统呈现为字符串,与输出呈现到控制台的方式相同。

ps.AddScript("ls | Out-String"); // Add a script

注:

  • Windows PowerShell 假定固定行宽为 120 个字符,并带有(隐含的)表格 (Format-Table) 或 wide (Format-Wide) 格式,截断较长的行(除非输出对象的类型为 [string]),截断点用 ...

    表示
  • PowerShell [Core] 7+ 从根本上表现出相同的行为,但仅使用默认宽度 120 作为 后备:当托管(控制台子系统)可执行文件在控制台(终端) 中为 运行 时, 控制台 window而是使用了宽度 ,这与您在常规 PowerShell 控制台 window 中获得的行为相同(参见 )。

要解决这个问题,将足够大的线宽传递给 -Width;例如:

ps.AddScript("ls | Out-String -Width 200");

注:

  • WindowsPowerShell中,不要使用-Width ([int]::MaxValue-1),因为每一行然后填充到该宽度,这将导致输出过大。

  • PowerShell [Core] 7+,不再进行padding,可以放心使用
    -Width ([int]::MaxValue-1)

一些旁白:

  • 为了健壮性,我建议避免在脚本和编译代码中使用别名(例如 ls 代替 Get-ChildItem)。 在手头的例子中,ls 不会在类 Unix 平台上工作,因为那里没有定义别名,以免与平台原生的 ls 实用程序冲突。

  • 最好将 PowerShell ps = PowerShell.Create(); 包裹在 using 块中,以确保处理掉 PowerShell 实例:using (PowerShell ps = PowerShell.Create()) { ... }

  • 通常不需要显式创建运行空间 - PowerShell.Create() 会为您创建一个。

  • PowerShell.Create()返回的System.Management.Automation.PowerShell实例直接暴露了.AddScript()等方法——不需要使用.Commands属性 .