我需要将外部进程的输出捕获到变量(字符串)中,因此我可以对其进行一些处理。
只要进程写入stdout,here的答案就能很好地解决。但是,如果进程失败,则会写入stderr。我也想捕捉这个字符串,我无法弄明白该怎么做。
示例:
$cmdOutput = (svn info) | out-string
这是有效的,除非SVN有错误。如果发生错误,SVN会写入stderr,$cmdOutput
为空。
如何捕获PowerShell中变量中写入stderr的文本?
答案 0 :(得分:5)
试试这个:
$cmdOutput = svn info 2>&1
答案 1 :(得分:1)
补充manojlds' helpful answer的概述:
简要说明重定向表达式2>&1
:
2>
重定向(>
)PowerShell的错误输出流,其编号为2
(并映射到stderr上)为({{1} })PowerShell的成功输出流,其编号为&
(并映射到stdout)。
运行Get-Help about_Redirection
了解详情。
作为输出行作为字符串的集合,无法分辨哪一行来自哪个流:
使用平台本机shell在源上执行合并使PowerShell只能看到 stdout 输出,它通常会收集字符串数组。
在以下示例中,每个命令都会创建stdout和stderr输出 所有命令都使用PSv3 +语法以方便和简洁。
Windows 示例:
1
Unix 示例(PowerShell核心):
# Collect combined output across both streams as an array of
# strings, where each string represents and output line.
# Note the selective quoting around & and 2>&1 to make sure they
# are passed through to cmd.exe rather than PowerShell itself interpreting them.
$allOutput = cmd /c ver '&' dir \nosuch '2>&1'
注意:
您需要显式调用平台本机shell(Windows上为# sh, the Unix default shell, expects the entire command as a *single* argument.
$allOutput = sh -c '{ date; ls /nosuch; } 2>&1'
,Unix上为cmd /c
),这使得此方法的可移植性降低。
您将无法从结果数组中判断哪条线来自哪条线 请参阅下文,了解如何进行区分。
混合使用 string 行(来自stdout)和sh -c
"行" (来自stderr):
通过使用 PowerShell&#39> [System.Management.Automation.ErrorRecord]
重定向,您还可以获得表示合并的stdout和stderr流的单个行集合,但不会捕获stderr行作为字符串,但作为2>&1
个实例。
警告:从Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.4开始,错误会导致意外行为重定向PowerShell&# 39;当[System.Management.Automation.ErrorRecord]
生效时2>
的错误流生效 - 请参阅this GitHub issue。
通过检查每个数组元素的数据类型,您可以灵活地区分stdout和stderr行。
另一方面,您可能必须将stderr行转换为字符串。
在PSv6中,当您只是输出捕获的输出时,不同的数据类型并不明显,但您可以通过反射来判断:
$ErrorActionPreference = 'Stop'
检查结果:
# Let PowerShell merge the streams with 2>&1, which captures
# stdout lines as strings and stderr lines as [System.Management.Automation.ErrorRecord]
# instances.
$allOutput = cmd /c ver '&' dir \nosuch 2>&1
如您所见,最后一个数组元素是一个错误记录,它表示由PS> $allOutput | % GetType | % Name
String
String
String
String
String
String
ErrorRecord
命令生成的单个File Not Found
stderr输出行。
注意:在PSv5.1之前,当您将捕获的输出输出到控制台时,dir \nosuch
实例实际呈现的格式与PowerShell错误相同,可能会使其看起来好像发生了错误然后的。
在PSv6中,这些错误记录只打印它们的消息,即原始stderr行的内容,而可视,你不能告诉它正在打印错误记录而不是字符串。 / p>
将所有捕获的输出转换为字符串 :
[System.Management.Automation.ErrorRecord]
要过滤掉stderr行(并在过程中将它们转换为字符串):
$allOutput = cmd /c ver '&' dir \nosuch 2>&1 | % ToString
使用临时文件 :
从PSv5.1开始,唯一直接捕获stderr输出的方法是使用重定向$allOutput = cmd /c ver '&' dir \nosuch 2>&1
$stderrOnly = $allOutput | ? { $_ -is [System.Management.Automation.ErrorRecord] } |
% ToString
和 filename 目标;即,在文件中捕获stderr输出 - 作为文本:
2>
显然,这很麻烦,也比内存操作慢。
使用PSv4 + $stderrFile = New-TemporaryFile # PSv5+; PSv4-: use [io.path]::GetTempFileName()
$stdoutOutput = cmd /c ver '&' dir \nosuch 2>$stderrFile
$stderrOutput = Get-Content $stdErrFile
Remove-Item $stderrFile
集合运算符:
(鲜为人知的)PSv4 + .Where()
集合运算符允许您根据元素是否通过布尔测试将集合拆分为两个:
.Where()
潜在的未来替代方案:
This GitHub issue提出了一种新的重定向语法,可以更简单地收集变量中的stderr行:
# Merge the streams first, so they go to the success stream, then
# split the objects in the merged stream by type.
$stdoutOutput, $stderrOutput = (cmd /c ver '&' dir \nosuch 2>&1).Where({
$_ -isnot [System.Management.Automation.ErrorRecord]
}, 'Split')
# Convert the collected [System.Management.Automation.ErrorRecord]
# instances to strings.
$stderrOutput = $stderrOutput | % ToString