简体   繁体   English

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

[英]PowerShell: Capture the output from external process that writes to stderr in a variable

I need to capture the output of a external process into a variable (string), so I can do some processing on that. 我需要将外部进程的输出捕获到变量(字符串)中,因此我可以对其进行一些处理。

The answer from here works nicely as long as the process writes to stdout. 只要进程写入stdout, 这里的答案可以很好地工作。 However, if the process fails, it writes to stderr. 但是,如果进程失败,则会写入stderr。 I'd like to capture this string too, and I can't figure out how to do it. 我也想捕捉这个字符串,我无法弄清楚如何做到这一点。

Example: 例:

$cmdOutput = (svn info) | out-string

This works, unless SVN has an error. 这是有效的,除非SVN有错误。 If an error occured, SVN writes to stderr, and $cmdOutput is empty. 如果发生错误,SVN会写入stderr, $cmdOutput为空。

How do I capture the text written to stderr in a variable in PowerShell ? 如何捕获PowerShell中变量中写入stderr的文本?

尝试这个:

$cmdOutput = svn info 2>&1

To complement manojlds' helpful answer with an overview: 通过概述补充manojlds的有用答案

To briefly explain redirection expression 2>&1 : 简要说明重定向表达式2>&1
2> redirects ( > ) PowerShell's error output stream, whose number is 2 (and which maps onto stderr) into ( & ) PowerShell's success output stream, whose number is 1 (and maps onto stdout). 2>重定向( > )PowerShell的错误输出流,其编号为2 (并映射到stderr)到( & )PowerShell的成功输出流中,其编号为1 (并映射到stdout)。
Run Get-Help about_Redirection to learn more. 运行Get-Help about_Redirection以了解更多信息。

Capturing stdout and stderr output combined , as a merged stream: 捕获stdout和stderr输出组合 ,作为合并流:


As a collection of output lines as strings , without the ability to tell which line came from what stream: 作为字符串输出行的集合,无法分辨哪个行来自哪个流:

Using the platform-native shell to perform the merging at the source makes PowerShell only see stdout output, which, as usual, it collects in an array of strings. 使用平台本机shell 在源处执行合并使PowerShell只能看到stdout输出,这通常会收集在字符串数组中。

In the following examples, the commands each create both stdout and stderr output. 在以下示例中,每个命令都创建stdout和stderr输出。
All commands use PSv3+ syntax for convenience and brevity. 所有命令都使用PSv3 +语法以方便和简洁。

Windows example: Windows示例:

# 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'

Unix example (PowerShell Core): Unix示例(PowerShell核心):

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

Note: 注意:

  • You do need to call the platform-native shell explicitly ( cmd /c on Windows, sh -c on Unix), which makes this approach less portable. 您需要显式调用平台本机shell(在Windows上为cmd /c ,在Unix上为sh -c ),这使得此方法不太可移植。

  • You will not be able to tell from the resulting array of lines which line came from what stream. 您将无法从结果数组中判断哪条线来自哪条线。
    See below for how to make this distinction. 请参阅下文,了解如何进行区分。


As a mix of string lines (from stdout) and [System.Management.Automation.ErrorRecord] "lines" (from stderr): 作为字符串行(来自stdout)和[System.Management.Automation.ErrorRecord] “行”(来自stderr)的混合:

By using PowerShell's 2>&1 redirection, you also get a single collection of lines representing the merged stdout and stderr streams, but the stderr lines aren't captured as strings , but as [System.Management.Automation.ErrorRecord] instances. 通过使用PowerShell的 2>&1重定向,您还可以获得表示合并的stdout和stderr流的单个行集合,但是stderr行不是作为字符串捕获的,而是作为[System.Management.Automation.ErrorRecord]实例捕获的。

Caveat : A bug as of Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.4 results in unexpected behavior when you redirect PowerShell's error stream with 2> while $ErrorActionPreference = 'Stop' is in effect - see this GitHub issue . 警告 :一个错误的意外行为的Windows PowerShell V5.1 / PowerShell核心V6.0.0-beta.4结果,当你重定向PowerShell的错误流2>$ErrorActionPreference = 'Stop'实际上是-看到这个GitHub的问题

This gives you the flexibility to distinguish between stdout and stderr lines by examining the data type of each array element. 这使您可以通过检查每个数组元素的数据类型来灵活地区分stdout和stderr行。
On the flip side, you may have to convert the stderr lines to strings. 另一方面,您可能必须将stderr行转换为字符串。

In PSv6, the different data types are not obvious when you simply output the captured output, but you can tell by reflection: 在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

Inspect the result: 检查结果:

PS> $allOutput | % GetType | % Name
String
String
String
String
String
String
ErrorRecord

As you can see, the last array element is an error record, which represent the single File Not Found stderr output line produced by the dir \\nosuch command. 如您所见,最后一个数组元素是一个错误记录,它表示由dir \\nosuch命令生成的单个File Not Found stderr输出行。

Note: Up to PSv5.1, when you output the captured output to the console, the [System.Management.Automation.ErrorRecord] instances actually rendered in the same format as PowerShell errors, perhaps making it appear as if an error had occurred then . 注:截至PSv5.1,当你输出的拍摄输出到控制台,在[System.Management.Automation.ErrorRecord]在相同的格式错误的PowerShell实际呈现,或许情况使得它看上去就像发生错误,则
In PSv6, these error records print just their message, ie, the contents of the original stderr line, and visually you can't tell that an error record rather than a string is being printed. 在PSv6中,这些错误记录只打印它们的消息,即原始stderr行的内容,并且在视觉上你不能告诉错误记录而不是字符串正在打印。

To convert all captured output to strings : 要将所有捕获的输出转换为字符串

$allOutput = cmd /c ver '&' dir \nosuch 2>&1 | % ToString

To filter out the stderr lines (and converting them to strings in the process): 过滤掉stderr行 (并在过程中将它们转换为字符串):

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

Capturing stderr output separately : 分别捕获stderr输出:


Using a temporary file : 使用临时文件

As of PSv5.1, the only direct way to capture stderr output in isolation is to use redirection 2> with a filename target; 从PSv5.1开始,唯一直接捕获stderr输出的方法是使用重定向2>文件名目标; ie, to capture stderr output - as text - in a file : 即,在文件中捕获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

Clearly, this is cumbersome and also slower than in-memory operations. 显然,这比内存操作更麻烦,也更慢。


Using the PSv4+ .Where() collection operator : 使用PSv4 + .Where()集合运算符

The (little-known) PSv4+ .Where() collection operator allows you to split a collection in two, based on whether the elements pass a Boolean test or not: (鲜为人知的)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

Potential future alternative: 潜在的未来选择:

This GitHub issue proposes a new redirection syntax that would allow collecting stderr lines in a variable much more simply: 这个GitHub问题提出了一种新的重定向语法,它允许更简单地收集变量中的stderr行:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM