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

dri*_*iis 4 powershell powershell-2.0

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

只要进程写入stdout ,这里的答案可以很好地工作.但是,如果进程失败,则会写入stderr.我也想捕捉这个字符串,我无法弄清楚如何做到这一点.

例:

$cmdOutput = (svn info) | out-string
Run Code Online (Sandbox Code Playgroud)

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

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

man*_*lds 6

试试这个:

$cmdOutput = svn info 2>&1
Run Code Online (Sandbox Code Playgroud)

  • @driis 1是标准输出描述符,2是错误输出.使用2>&1,我们将错误重定向到标准输出.如果它有效,你应该接受它作为答案.即使在您链接到的其他问题中也是如此. (2认同)

mkl*_*nt0 5

用概述补充manojlds 的有用答案

简单解释重定向表达式2>&1
2>将 ( >) PowerShell 的错误输出流,其编号为2(并映射到 stderr)重定向到 ( &) PowerShell 的成功输出流,其编号为1(并映射到 stdout)。
运行Get-Help about_Redirection以了解更多信息。

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


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

使用平台原生 shell在源执行合并使 PowerShell 只能看到stdout输出,像往常一样,它收集在一个字符串数组中。

在以下示例中,每个命令都会创建 stdout 和 stderr 输出。
为了方便和简洁,所有命令都使用 PSv3+ 语法。

窗口示例:

# 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'
Run Code Online (Sandbox Code Playgroud)

Unix示例(PowerShell 核心):

# sh, the Unix default shell, expects the entire command as a *single* argument.
$allOutput = sh -c '{ date; ls /nosuch; } 2>&1'
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 您确实需要显式调用平台原生 shell(cmd /c在 Windows 上,sh -c在 Unix 上),这使得这种方法的可移植性降低。

  • 您将无法从生成的行数组中分辨出哪一行来自哪个流。
    有关如何进行这种区分,请参见下文。


作为混合 字符串行(来自标准输出)和[System.Management.Automation.ErrorRecord]“行”(来自标准错误)的混合:

通过使用PowerShell 的 2>&1重定向,您还可以获得代表合并的 stdout 和 stderr 流的单个行集合,但 stderr 行不会被捕获为字符串,而是作为[System.Management.Automation.ErrorRecord]实例。

警告:一个错误的的Windows PowerShell V5.1的/意外的行为PowerShell核心V6.0.0-beta.4结果时重定向使用PowerShell的错误流2>,而$ErrorActionPreference = 'Stop'生效-看到这个GitHub的问题

这使您可以通过检查每个数组元素的数据类型,灵活地区分 stdout 和 stderr 行。
另一方面,您可能必须将 stderr 行转换为字符串。

在 PSv6 中,简单输出捕获的输出时,不同的数据类型并不明显,但您可以通过反射来判断:

# 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
Run Code Online (Sandbox Code Playgroud)

检查结果:

PS> $allOutput | % GetType | % Name
String
String
String
String
String
String
ErrorRecord
Run Code Online (Sandbox Code Playgroud)

如您所见,最后一个数组元素是一个错误记录,它表示命令File Not Found产生的单个stderr 输出行dir \nosuch

注:截至PSv5.1,当你输出的拍摄输出到控制台中,[System.Management.Automation.ErrorRecord]在相同的格式错误的PowerShell实际呈现,也许使它看起来就像发生了错误情况,然后
在 PSv6 中,这些错误记录只打印它们的消息,即原始 stderr 行的内容,并且在视觉上您无法分辨出正在打印的是错误记录而不是字符串。

为了所有捕获的输出转换为字符串

$allOutput = cmd /c ver '&' dir \nosuch 2>&1 | % ToString
Run Code Online (Sandbox Code Playgroud)

过滤掉 stderr 行(并在此过程中将它们转换为字符串):

$allOutput = cmd /c ver '&' dir \nosuch 2>&1
$stderrOnly = $allOutput | ? { $_ -is [System.Management.Automation.ErrorRecord] } | 
  % ToString
Run Code Online (Sandbox Code Playgroud)

捕获stderr输出分别


使用临时文件

作为PSv5.1的,只有直接的方式捕获stderr输出隔离是使用重定向2>一个文件名的目标; 即,在文件中捕获 stderr 输出 - 作为文本:

$stderrFile = New-TemporaryFile # PSv5+; PSv4-: use [io.path]::GetTempFileName()
$stdoutOutput = cmd /c ver '&' dir \nosuch 2>$stderrFile
$stderrOutput = Get-Content $stdErrFile
Remove-Item $stderrFile
Run Code Online (Sandbox Code Playgroud)

显然,这很麻烦,而且比内存操作慢。


使用 PSv4+.Where()收集运算符

(鲜为人知的)PSv4+.Where()集合运算符允许您根据元素是否通过布尔测试将集合一分为二:

 # 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
Run Code Online (Sandbox Code Playgroud)

潜在的未来替代方案:

这个 GitHub 问题提出了一种新的重定向语法,它允许更简单地收集变量中的 stderr 行:

 # As of v5.1: WISHFUL THINKING
 $stdout = cmd /c ver '&' dir \nosuch 2>&stderr # collect stderr lines in var. $stderr
Run Code Online (Sandbox Code Playgroud)