我需要将外部进程的输出捕获到一个变量(字符串)中,这样我就可以对它进行一些处理。
只要进程写入stdout,来自here的答案就能很好地工作。但是,如果进程失败,它将写入stderr。我也想捕获这个字符串,但我不知道该怎么做。
示例如下:
$cmdOutput = (svn info) | out-string
字符串
除非SVN有错误,否则这是可行的。如果发生错误,SVN会写入stderr,$cmdOutput
为空。
如何在PowerShell的变量中捕获写入stderr的文本?
我需要将外部进程的输出捕获到一个变量(字符串)中,这样我就可以对它进行一些处理。
只要进程写入stdout,来自here的答案就能很好地工作。但是,如果进程失败,它将写入stderr。我也想捕获这个字符串,但我不知道该怎么做。
示例如下:
$cmdOutput = (svn info) | out-string
字符串
除非SVN有错误,否则这是可行的。如果发生错误,SVN会写入stderr,$cmdOutput
为空。
如何在PowerShell的变量中捕获写入stderr的文本?
2条答案
按热度按时间0ejtzxu11#
试试这个:
字符串
cvxl0en22#
为了补充manojlds' helpful answer的概述:
简单解释一下重定向表达式
2>&1
:2>
重定向(>
)PowerShell的 error 输出流,其编号为2
(并Map到标准错误)到(&
)PowerShell的 success 输出流,其编号为1
(并Map到标准输出)。运行
Get-Help about_Redirection
了解更多信息。字符编码注意事项,适用于以下所有解决方案:
[Console]::OutputEncoding
所反映的那样,它本身默认为控制台的活动代码页(如chcp
所报告的那样)。[Console]::OutputEncoding
设置为实际编码,以确保输出中非ASCII字符的正确解释-有关详细信息,请参阅this answer。捕获stdout和stderr输出 combined,作为合并流:
*作为输出行的集合 * 作为字符串 ,无法区分哪一行来自哪个流:
使用平台原生shell在源代码中执行合并 *,使PowerShell只能看到 stdout 输出,像往常一样,它收集在字符串数组中。
在下面的示例中,每个命令都创建stdout和stderr输出。
为了方便和简洁,所有命令都使用PSv 3+语法。
字符串
型
注意事项:
cmd /c
,Unix上的sh -c
),这使得这种方法的可移植性较差。下面我们来看看如何进行这种区分。
作为 string lines(来自stdout)和
[System.Management.Automation.ErrorRecord]
“lines”(来自stderr)的混合:通过使用 *PowerShell的 *
2>&1
重定向,您还可以获得表示合并的stdout和stderr流的单个行流,但 stderr 行不会被捕获为 strings,而是作为[System.Management.Automation.ErrorRecord]
示例。注意事项:Windows PowerShell v5.1中的一个bug(自PowerShell (Core) 7+中修复后)在
$ErrorActionPreference = 'Stop'
生效时使用2>
重定向PowerShell的错误流时会导致意外行为-请参阅this GitHub issue。这使您可以通过检查每个输出对象的数据类型来灵活地区分stdout和stderr行。
另一方面,您可能必须将stderr行转换为字符串。
在PowerShell (Core) 7+中,当你只是 * 显示 * 捕获的输出时,不同的数据类型并不明显,但是你可以通过反射来分辨:
型
检查结果(
%
是ForEach-Object
cmdlet的内置别名):型
可以看到,最后一个数组元素是一个错误记录,它表示
dir \nosuch
命令生成的单个File Not Found
stderr输出行。注意事项:
[System.Management.Automation.ErrorRecord]
示例实际上以与PowerShell错误相同的格式呈现,可能会使其看起来好像发生了错误 * 然后 *。在 PowerShell(Core)7+ 中,这些错误记录只打印它们的消息,即原始stderr行的内容,并且 * 视觉上 * 您无法分辨正在打印的是错误记录而不是字符串。
将所有捕获的输出**转换为 * 字符串 *(为简洁起见,
%
,使用ForEach-Object
的内置别名;实际上,.ToString()
方法在每个输出对象上被调用):型
过滤掉stderr行(并在此过程中将其转换为字符串;为简洁起见,使用
?
,使用Where-Object
的内置别名):型
单独捕获stderr输出:
使用临时 * 文件*:
从PSv5.1开始,单独捕获stderr输出的唯一直接方法是使用带有 filename 目标的重定向
2>
;即,捕获stderr输出-作为文本-在 * 文件 * 中:型
显然,这是麻烦的,也比内存中的操作慢。
使用PSv 4+内部
.Where()
数组方法:PSv 4 + intrinsic
.Where()
数组方法允许您根据元素是否通过布尔测试将集合一分为二:型
注意事项:
[string[]]
类型约束将构成stderr输出的[System.Management.Automation.ErrorRecord]
示例转换为字符串数组。.Where()
发出的两个输出对象是 always 集合(类似数组,类型为[System.Collections.ObjectModel.Collection[psobject]]
),即使它们只包含 * 一个 * 元素。虽然这种方法既方便又简洁,但其缺点是stdout输出也会首先在内存中完整收集,并且 * 然后 * 必须输出以便打印到控制台(如果需要),这否定了PowerShell通常的输出 * 流 * 的好处:中继输出线 *,因为它们正在被接收 *;如果stdout不需要作为一个整体收集在内存中,除了可能浪费内存之外,这在长期运行的外部程序中可能是不希望的,因为持续的视觉反馈很重要;请参阅下一个解决方案。
将stderr输出的内存捕获与 streaming stdout结合使用:
这种方法避免了
.Where()
解决方案的collect-all-stdout-first行为,而是在接收到stdout行时对其进行流式传输:型
未来可能的改进:
GitHub issue #4332建议扩展重定向语法,以便更简单地在 variable 中收集stderr行:
型