如何在PowerShell中将输出捕获到外部进程的变量中?

时间:2011-11-11 16:59:11

标签: powershell process

我想运行一个外部进程并将其命令输出捕获到PowerShell中的变量。我目前正在使用它:

$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait

我已经确认命令正在执行但我需要将输出捕获到变量中。这意味着我无法使用-RedirectOutput,因为它只会重定向到文件。

11 个答案:

答案 0 :(得分:127)

你试过了吗?

$OutputVariable = (Shell command) | Out-String

答案 1 :(得分:122)

注意:问题中的命令使用Start-Process,这会阻止直接捕获目标程序的输出。通常,不要使用Start-Process同步执行控制台应用程序 - 只需直接调用 ,就像在任何shell中一样。这样做可以使应用程序连接到调用控制台的标准流,允许通过简单的赋值$output = netdom ...捕获其输出,如下所述。

从根本上,从外部实用程序捕获输出的工作方式与使用PowerShell本机命令相同(您可能需要复习{{3 }}):

$cmdOutput = <command>   # captures the command's success stream / stdout

请注意,如果$cmdOutput生成多个输出对象<command>会收到对象的数组,如果是 >外部程序表示包含程序输出的字符串数组。
如果您希望$cmdOutput始终收到 - 可能是多行 - 字符串,请使用
$cmdOutput = <command> | Out-String

以变量 打印到的捕获输出:

<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed

或者,如果<command> cmdlet 高级功能,则可以使用how to execute external tools

<command> -OutVariable cmdOutput   # cmdlets and advanced functions only

请注意,对于-OutVariable,与其他方案不同,$cmdOutput 总是 集合,即使只有一个< / em>对象输出。具体来说,返回类似数组[System.Collections.ArrayList]类型的实例 有关此差异的讨论,请参阅common parameter
-OutVariable / -ov

要捕获 多个命令的输出,请使用子表达式($(...))或使用{调用脚本块({ ... }) {1}}或&

.

请注意一般需要使用$cmdOutput = $(<command>; ...) # subexpression $cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars. $cmdOutput = . {<command>; ...} # script block with . - no child scope (调用运算符)作为名称/路径引用的单个命令 的前缀 - 例如{{1 - 与外部程序本身无关(它同样适用于PowerShell脚本),但是 语法要求:PowerShell解析以带引号的字符串开头的语句默认情况下,在表达式模式中,而参数模式需要调用命令(cmdlet,外部程序,函数,别名),这是&确保的。 / p>

$cmdOutput = & 'netdom.exe' ...& / $(...)之间的主要区别在于前者在将其作为一个整体返回之前收集内存中的所有输入,而后者 stream 输出,适用于逐个流水线处理。

重定向也从根本上起作用(但请参见下面的警告):

& { ... }

但是,对于外部命令,以下内容更有可能按预期工作:

. { ... }

特定于外部计划的注意事项:

  • 外部程序,因为它们在PowerShell的类型系统之外运行,仅通过其成功流返回字符串 (标准输出)。

  • 如果输出包含超过1行,则默认情况下,PowerShell会将其拆分为 数组字符串。更准确地说,输出行存储在$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1) 类型的数组中,其元素是字符串($cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below. )。

  • 如果希望输出为,可能是多行字符串要管道到{{ 1}}
    [System.Object[]]

  • 使用[System.String] 将stderr重定向到stdout,以便将其作为成功流的一部分进行捕获,附带警告

    • 要使Out-String合并stdout和stderr 在源 $cmdOutput = <command> | Out-String处理重定向,使用以下习语:
      2>&1
      2>&1

      • cmd.exe使用$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)命令调用$cmdOutput = cmd /c <command> '2>&1' | Out-String # single string,并在cmd /c完成后退出。
      • 请注意cmd.exe周围的单引号,以确保重定向传递给<command>,而不是由PowerShell解释。
      • 请注意,涉及<command>意味着除了PowerShell自己的要求之外,默认情况下规则用于转义字符和扩展环境变量。在PS v3 +中你可以使用特殊参数2>&1(所谓的停止解析符号)来关闭PowerShell对剩余参数的解释,除了cmd.exe - 样式环境变量引用,例如cmd.exe

      • 请注意,由于您使用此方法将stdout和stderr 合并到源您无法区分stdout-originated和PowerShell中的stderr-originated lines ;如果您确实需要这种区别,请使用PowerShell自己的--%重定向 - 请参阅下文。

    • 使用 PowerShell&#39> cmd.exe重定向来了解哪些行来自哪个流

      • Stderr 输出被捕获为错误记录%PATH%),而非字符串,因此输出数组可能包含混合 字符串(每个字符串代表stdout行)和错误记录(每条记录代表stderr行)。请注意,根据2>&1的请求,字符串和错误记录都是通过PowerShell的成功输出流接收的。

      • 在控制台中,错误记录以红色打印, 1st 默认生成多行显示,以与cmdlet的非终止错误显示相同的格式; 后续错误记录也以红色打印,但仅在单行上打印错误消息

      • 输出到控制台时,字符串通常首先在输出数组中,然后是错误记录(at至少在一批stdout / stderr行输出&#34;同时&#34;),但幸运的是,当你捕获输出时,它是正确交错的,使用相同的输出顺序,没有2>&1;换句话说:输出到控制台时,捕获的输出不会反映外部命令生成stdout和stderr行的顺序。

        < / LI>
      • 如果您使用[System.Management.Automation.ErrorRecord] 单个字符串中捕获整个输出, PowerShell将添加额外行 ,因为错误记录的字符串表示包含额外信息,例如位置(2>&1)和类别(2>&1);奇怪的是,这仅适用于第一个错误记录。

        • 要解决此问题,请将Out-String方法应用于每个输出对象,而不是管道到At line:...
          + CategoryInfo ...;
          在PS v3 +中你可以简化为:
          .ToString()
          (作为奖励,如果没有捕获输出,即使打印到控制台,这也会产生正确的交错输出。)
      • 或者,过滤错误记录 out 并将其发送到PowerShell的错误流Out-String (作为奖励,如果输出未被捕获,即使在打印到控制台时也会产生正确的交错输出:

  
$cmdOutput = <command> 2>&1 | % { $_.ToString() }

答案 2 :(得分:19)

如果你想重定向错误输出,你必须这样做:

$cmdOutput = command 2>&1

或者,如果程序名称中包含空格:

$cmdOutput = & "command with spaces" 2>&1

答案 3 :(得分:9)

或试试这个。它会将输出捕获到变量$ scriptOutput:

& "netdom.exe" $params | Tee-Object -Variable scriptOutput | Out-Null

$scriptOutput

答案 4 :(得分:9)

另一个现实生活中的例子:

$result = & "$env:cust_tls_store\Tools\WDK\x64\devcon.exe" enable $strHwid 2>&1 | Out-String

请注意,此示例包含一个路径(以环境变量开头)。请注意,引号必须包含路径和EXE文件,而不是参数!

注意:不要忘记命令前面的&字符,但在引号之外。

还会收集错误输出。

我花了一段时间才使这个组合起作用,所以我想我会分享它。

答案 5 :(得分:2)

我尝试了答案,但在我的情况下,我没有得到原始输出。相反,它被转换为PowerShell例外。

我得到的原始结果:

$rawOutput = (cmd /c <command> 2`>`&1)

答案 6 :(得分:0)

如果您要做的只是捕获命令的输出,那么这将很有效。

我用它来改变系统时间,因为[timezoneinfo]::local总是产生相同的信息,甚至之后你对系统进行了更改。这是我可以验证并记录时区变化的唯一方法:

$NewTime = (powershell.exe -command [timezoneinfo]::local)
$NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append

这意味着我必须打开一个新的 PowerShell 会话来重新加载系统变量。

答案 7 :(得分:0)

这东西对我有用:

$scriptOutput = (cmd /s /c $FilePath $ArgumentList)

答案 8 :(得分:0)

我有以下工作要做:

$Command1="C:\\ProgramData\Amazon\Tools\ebsnvme-id.exe"
$result = & invoke-Expression $Command1 | Out-String

$结果为您提供了有需要的

答案 9 :(得分:0)

我使用以下内容:

Function GetProgramOutput([string]$exe, [string]$arguments)
{
    $process = New-Object -TypeName System.Diagnostics.Process
    $process.StartInfo.FileName = $exe
    $process.StartInfo.Arguments = $arguments
    
    $process.StartInfo.UseShellExecute = $false
    $process.StartInfo.RedirectStandardOutput = $true
    $process.StartInfo.RedirectStandardError = $true
    $process.Start()
    
    $output = $process.StandardOutput.ReadToEnd()   
    $err = $process.StandardError.ReadToEnd()
    
    $process.WaitForExit()
    
    $output
    $err
}
    
$exe = "C:\Program Files\7-Zip\7z.exe"
$arguments = "i"
    
$runResult = (GetProgramOutput $exe $arguments)
$stdout = $runResult[-2]
$stderr = $runResult[-1]
    
[System.Console]::WriteLine("Standard out: " + $stdout)
[System.Console]::WriteLine("Standard error: " + $stderr)

答案 10 :(得分:0)

对我来说有什么技巧,在使用外部命令以及标准错误标准输出流都可能是运行命令的结果时会起作用(或它们的混合),如下所示:

$output = (command 2>&1)