打印命令输出而不是在Powershell中捕获命令

时间:2018-08-30 14:47:15

标签: powershell stdout subcommand

通常,我知道如何将命令的输出捕获到Powershell中的变量中:

> $output = python3 --version
> Write-Host $output
Python 3.7.0

Python 3打印其版本,但已捕获到变量$output中,并且控制台上未显示任何内容。如预期:

> $output = python3 --version
> Write-Host $output
Python 3.7.0

但是对于Python 2,它不起作用。 Python 2会打印其版本,但会在控制台上显示而不是捕获,并且变量$output为空。

> $output = python2 --version
Python 2.7.15
> Write-Host $output

>

我尝试了一些其他语法,但到目前为止没有任何帮助:

> $output = (python2 --version)  # use a subcommand
Python 2.7.15
> $output = "$(python2 --version)"  # enclose the subcommand into a string
Python 2.7.15

如果我在Write-Command中使用它,输出将如下所示:

> Write-Host "Python version: $(python2 --version)"
Python 2.7.15
Python version:
>

Python 2如何打印其未被捕获到变量中的版本,并且有可能以任何方式捕获它?

1 个答案:

答案 0 :(得分:3)

这是由 Python 2导致的,该版本将版本打印到stderr而不是stdout 。程序调用输出的分配仅捕获stdout,在这种情况下为空。另一方面,默认情况下将stderr打印到控制台,这就是$output变量为空并且版本被打印到控制台的原因。有关详情,请参见下文。

tl;医生:

# Redirect stderr to the success output stream and convert to a string.
$output = (python --version 2>&1).ToString()
# $? is now $True if python could be invoked, and $False otherwise

对于可能返回多条 stderr行的命令,请使用:

  • PSv4 +,使用.ForEach() 方法

    $output = (python --version 2>&1).ForEach('ToString')
    
  • PSv3-,使用ForEach-Object cmdlet (其内置别名为%):

    # Note: The (...) around the python call are required for
    #       $? to have the expected value.
    $output = (python --version 2>&1) | % { $_.ToString() }
    
    # Shorter PSv3+ equivalent, using an operation statement
    $output = (python --version 2>&1) | % ToString
    

  • 正如Mathias R. Jessen在对问题的评论中指出的那样,您自己根据版本进行了说明, python 2.x -令人惊讶的是-输出它的版本信息是 stderr 而不是stdout,与 3.x 不同。

  • 您无法使用$output = ...捕获输出的原因是外部程序调用的输出分配给变量只能捕获其 stdout 输出

    • 使用 PowerShell本机命令,它捕获命令的成功输出流-请参见about_redirection
  • 主要解决方法是使用重定向2>&1 合并PowerShell的错误流(如果将Powerder的类似物重定向到stderr,则将stderr输出映射到 >)到PowerShell的成功输出流(标准输出模拟)中,然后将其捕获到变量中。
    但是,这有两个副作用:

    • 由于2>&1重定向现在会涉及PowerShell的错误流(默认情况下,stderr输出会传递到控制台),所以 {{1} }始终设置为$? ,因为将任何内容写入错误流都可以做到这一点(即使最终将错误流发送到其他地方,在这种情况下)。

      • 请注意,在没有重定向的情况下,$False未设置为$? (除了ISE中的{{3 }}),因为stderr输出将永远不会“接触” PowerShell的错误输出流。
        在PowerShell控制台中,调用没有进行$False重定向的外部程序时,如果{em> exit退出,则2>设置为$? 代码
        非零$False)。

      • 因此,使用$LASTEXITCODE -ne 0解决了一个问题(无法捕获输出),同时引入了另一个问题(2>&1错误地设置为$?)-有关补救措施,请参见下文。

      • p>
    • 不是字符串写入成功输出流,而是$False实例,因为PowerShell将每条stderr输出行都包装在其中一个。

    • 如果仅在 string 上下文中使用这些实例,例如[System.Management.Automation.ErrorRecord],则可能不会注意到或不在意。

  • "Version: $output"(对于单条输出线)和.ToString() / .ForEach('ToString')(对于多条输出线)可消除两种副作用:

    • 它们将(...) | % ToString实例转换回字符串

    • 他们将[System.Management.Automation.ErrorRecord]重置为$?,因为$True / .ToString()调用/在命令周围使用.ForEach()表达式,即使它们包含将自身设置为(...)$?命令,它们也被认为是成功的。

注意:

  • 如果找不到可执行文件$False,PowerShell本身将引发语句终止错误,这意味着对python的赋值将被跳过( (其值(如果存在)将不会更改),默认情况下,您会收到嘈杂的错误输出,并且$output将设置为$?

  • 如果可以调用$False 并且报告错误(对于python则不太可能),如非零所示退出代码,您可以通过自动--version变量检查退出代码(其值在下次调用外部程序之前不会更改)。< / p>

  • 利用$LASTEXITCODE到命令周围这样简单的操作使(...)总是返回$?(除非发生语句或脚本终止错误),在上述方法中,但通常是模糊的行为,可能被认为是有问题的-请参见an unfortunate discrepancy