1

一般来说,我知道如何将命令的输出捕获到 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 如何打印它没有被捕获到变量中的版本,并且无论如何都可以捕获它?

4

1 回答 1

3

它是由Python 2 引起的,它打印 versionstderr而不是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在对该问题的评论中指出的那样,并且您自己在版本方面进行了澄清,与3.x不同, python 2.x - 令人惊讶的是 - 将其版本信息输出到stderr而不是 stdout 。

  • 您无法捕获输出的原因$output = ...外部程序调用的输出分配给变量默认情况下仅捕获其标准输出输出

    • 使用PowerShell 原生命令,它会捕获命令的成功输出流- 请参阅about_redirection
  • 主要修复方法是使用重定向2>&1将 PowerShell 的错误流(PowerShell 的模拟到 stderr,如果重定向,则 stderr 输出映射到该流)合并到 PowerShell 的成功输出流(stdout 模拟),然后在变量中捕获。
    然而,这有两个副作用:

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

      • 请注意,在没有$?重定向的情况下不会设置为(在 ISE 中除外,这是一个不幸的差异),因为 stderr 输出永远不会“触及”PowerShell 的错误输出流。在 PowerShell 控制台中,在没有重定向的情况下调用外部程序时,当其退出代码非零( ) 时才设置为is $False
        2>$?$False $LASTEXITCODE -ne 0

      • 因此,使用2>&1解决了一个问题(无法捕获输出),同时引入了另一个问题($?错误设置为$False) - 请参阅下面的补救措施。

    • 写入成功输出流的不是字符串,而是[System.Management.Automation.ErrorRecord]实例,因为 PowerShell 将每个 stderr 输出行包装为一个。

    • 但是,如果您仅在字符串上下文中使用这些实例- 例如"Version: $output",您可能不会注意到或关心。

  • .ToString()(对于单个输出行)和.ForEach('ToString')/ (...) | % ToString(对于可能的多个输出行)撤消两个副作用:

    • 他们将[System.Management.Automation.ErrorRecord]实例转换回字符串

    • 它们重置$?$True,因为命令周围的.ToString()/ .ForEach()calls / 使用是表达式,它们本身被认为是成功的,即使它们包含自己设置为的命令(...)$?$False

笔记:

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

  • 如果python 可以调用并且报告错误(这不太可能--version),如通过非零退出代码指示的那样,您可以通过自动变量检查该退出代码(其值在下一次调用外部程序$LASTEXITCODE之前不会改变)。

  • 在上述方法中利用了像放置(...)命令这样简单的东西$?总是返回$True(除非发生语句或脚本终止错误),但通常是可能被认为有问题的模糊行为 - 请参阅GitHub 上的讨论.

于 2018-08-30T16:02:44.450 回答