打印命令输出而不是在 Powershell 中捕获
Command output printed instead of captured in Powershell
一般来说,我知道如何在 Powershell 中将命令的输出捕获到变量中:
> $output = python3 --version
> Write-Host $output
Python 3.7.0
Python 3 打印它的版本,但它被捕获到变量 $output
中,控制台上没有显示任何内容。不出所料:
> $output = python3 --version
> Write-Host $output
Python 3.7.0
但是在Python2的情况下,就不行了。 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 如何打印其未捕获到变量中的版本,是否有可能捕获它?
这是由 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
对于可能 return 多个 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 而不是标准输出,不像 3.x.
您无法使用 $output = ...
捕获输出的原因是 分配了一个 外部程序 调用的输出默认情况下,变量仅捕获其 stdout 输出。
- 使用 PowerShell 本地命令,它捕获命令的成功输出流 - 参见about_redirection。
主要修复是使用重定向 2>&1
合并 PowerShell 的错误流(PowerShell 对 stderr 的模拟,stderr 输出映射到 if redirected) 到 PowerShell 的成功输出流(stdout 模拟),然后在变量中捕获。
但是,这有两个副作用:
由于 2>&1
重定向现在涉及 PowerShell 的错误流(默认情况下,stderr 输出 传递到控制台 ), $?
总是设置为 $False
,因为向错误流写入任何内容都会这样做(即使错误流最终被发送到其他地方,如本例所示) .
请注意 $?
而不是 设置为 $False
没有 重定向 (除了在 ISE 中,它是 an unfortunate discrepancy),因为 stderr 输出永远不会 "touches" PowerShell 的错误输出流。
在 PowerShell 控制台中,在没有 2>
重定向的情况下调用外部程序时,$?
设置为 $False
只有 是如果它的退出代码是非零($LASTEXITCODE -ne 0
)。
因此,使用 2>&1
解决了一个问题(无法捕获输出),同时引入了另一个问题($?
错误地设置为 $False
)- 请参阅下面的补救措施。
写入成功输出流的不是字符串,而是[System.Management.Automation.ErrorRecord]
实例,因为 PowerShell 将每个 stderr 输出行包装在一个中。
如果您仅在 string 上下文中使用这些实例 - 例如,"Version: $output"
,您可能不会注意到或关心,但是。
.ToString()
(对于单个输出行)和 .ForEach('ToString')
/ (...) | % ToString
(对于可能的多个输出行)撤消两个副作用:
他们将 [System.Management.Automation.ErrorRecord]
实例转换回 strings。
他们将$?
重置为$True
,因为.ToString()
/.ForEach()
调用/使用(...)
命令是表达式,它们本身被认为是成功的,即使它们包含命令,它们本身将$?
设置为$False
。
注:
如果无法找到可执行文件 python
,PowerShell 本身将抛出语句终止错误,这意味着对 $output
的赋值将被 跳过(它的值,如果存在的话,不会改变),默认情况下你会得到嘈杂的错误输出并且 $?
将被设置为 $False
.
如果python
可以被调用并且它报告一个错误(这不太可能与--version
),如非零 退出代码 所示,您可以通过自动 $LASTEXITCODE
变量检查该退出代码(其值直到下一次调用外部程序).
像在命令周围放置 (...)
这样简单的东西使得 $?
总是 return $True
(除非语句或脚本 -发生终止错误)在上述方法中被利用,但通常是可能被认为有问题的模糊行为 - 参见 this discussion on GitHub.
一般来说,我知道如何在 Powershell 中将命令的输出捕获到变量中:
> $output = python3 --version
> Write-Host $output
Python 3.7.0
Python 3 打印它的版本,但它被捕获到变量 $output
中,控制台上没有显示任何内容。不出所料:
> $output = python3 --version
> Write-Host $output
Python 3.7.0
但是在Python2的情况下,就不行了。 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 如何打印其未捕获到变量中的版本,是否有可能捕获它?
这是由 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
对于可能 return 多个 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 而不是标准输出,不像 3.x.您无法使用
$output = ...
捕获输出的原因是 分配了一个 外部程序 调用的输出默认情况下,变量仅捕获其 stdout 输出。- 使用 PowerShell 本地命令,它捕获命令的成功输出流 - 参见about_redirection。
主要修复是使用重定向
2>&1
合并 PowerShell 的错误流(PowerShell 对 stderr 的模拟,stderr 输出映射到 if redirected) 到 PowerShell 的成功输出流(stdout 模拟),然后在变量中捕获。
但是,这有两个副作用:由于
2>&1
重定向现在涉及 PowerShell 的错误流(默认情况下,stderr 输出 传递到控制台 ),$?
总是设置为$False
,因为向错误流写入任何内容都会这样做(即使错误流最终被发送到其他地方,如本例所示) .请注意
$?
而不是 设置为$False
没有 重定向 (除了在 ISE 中,它是 an unfortunate discrepancy),因为 stderr 输出永远不会 "touches" PowerShell 的错误输出流。
在 PowerShell 控制台中,在没有2>
重定向的情况下调用外部程序时,$?
设置为$False
只有 是如果它的退出代码是非零($LASTEXITCODE -ne 0
)。因此,使用
2>&1
解决了一个问题(无法捕获输出),同时引入了另一个问题($?
错误地设置为$False
)- 请参阅下面的补救措施。
写入成功输出流的不是字符串,而是
[System.Management.Automation.ErrorRecord]
实例,因为 PowerShell 将每个 stderr 输出行包装在一个中。如果您仅在 string 上下文中使用这些实例 - 例如,
"Version: $output"
,您可能不会注意到或关心,但是。
.ToString()
(对于单个输出行)和.ForEach('ToString')
/(...) | % ToString
(对于可能的多个输出行)撤消两个副作用:他们将
[System.Management.Automation.ErrorRecord]
实例转换回 strings。他们将
$?
重置为$True
,因为.ToString()
/.ForEach()
调用/使用(...)
命令是表达式,它们本身被认为是成功的,即使它们包含命令,它们本身将$?
设置为$False
。
注:
如果无法找到可执行文件
python
,PowerShell 本身将抛出语句终止错误,这意味着对$output
的赋值将被 跳过(它的值,如果存在的话,不会改变),默认情况下你会得到嘈杂的错误输出并且$?
将被设置为$False
.如果
python
可以被调用并且它报告一个错误(这不太可能与--version
),如非零 退出代码 所示,您可以通过自动$LASTEXITCODE
变量检查该退出代码(其值直到下一次调用外部程序).像在命令周围放置
(...)
这样简单的东西使得$?
总是 return$True
(除非语句或脚本 -发生终止错误)在上述方法中被利用,但通常是可能被认为有问题的模糊行为 - 参见 this discussion on GitHub.