PowerShell:从外部进程捕获输出,该输出在变量中写入stderr

时间:2011-11-18 15:33:00

标签: powershell powershell-v2.0

我需要将外部进程的输出捕获到变量(字符串)中,因此我可以对其进行一些处理。

只要进程写入stdout,here的答案就能很好地解决。但是,如果进程失败,则会写入stderr。我也想捕捉这个字符串,我无法弄明白该怎么做。

示例:

$cmdOutput = (svn info) | out-string

这是有效的,除非SVN有错误。如果发生错误,SVN会写入stderr,$cmdOutput为空。

如何捕获PowerShell中变量中写入stderr的文本?

2 个答案:

答案 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了解详情。

捕获stdout和stderr输出组合,作为合并流:

作为输出行作为字符串的集合,无法分辨哪一行来自哪个流:

使用平台本机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

分别捕获stderr输出

使用临时文件

从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