我在PowerShell脚本中使用了一些GIT命令。大多数时候,我通过Invoke-Expression
调用 GIT 命令,以便我,例如
- 可以解析输出,或/和
- 将输出转发到日志记录方法。
在某些 GIT 命令中,我认识到并非所有输出都通过Invoke-Expression
返回,尽管文档指出:
输出
PSObject
返回由调用的命令生成的输出(Command 参数的值)。
下面是一个示例:
> $x = iex "git fetch --all"
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 3), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
$x
内容:
> $x
Fetching origin
Fetching upstream
所以主要信息不会返回给$x
.我无法想象git fetch --all
通过stderr
返回主要信息(没有意义......
我还发现了这个PowerShell问题,这个问题没有答案,使用的PowerShell版本是2。
使用的PowerShell版本:
> $PSVersionTable
Name Value
---- -----
PSVersion 6.2.0
PSEdition Core
GitCommitId 6.2.0
OS Microsoft Windows 10.0.18362
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
如何强制Invoke-Expression
返回整个输出?
感谢
正如我在"PowerShell Capture Git Output"中提到的,在 Git 2.16(2018 年第 1 季度)中,您可以先尝试设置:
set GIT_REDIRECT_STDERR=2>&1
然后在Powershell脚本中,你应该同时获得stdout和stderr输出,
另请参阅dahlbyk/posh-git
问题 109 以获取更类似于 Powershell 的示例:
$env:GIT_REDIRECT_STDERR = '2>&1'
VonC 的答案适用于git
,特别是,但值得讨论一个通用解决方案:
注意:通常应避免使用Invoke-Expression
,并且没有理由将其用于调用外部程序:只需直接调用它们并分配给变量:
$capturedStdout = git ... # capture git's stdout output as an array of lines
如前所述,git
将状态信息输出到stderr,而数据则输出到 stdout;PowerShell 变量赋值仅捕获标准输出输出。[1]
要捕获 stdout 和stderr 的组合,交错,就像打印到终端一样,您可以使用重定向2>&1
,就像在其他 shell 中一样,将错误流/stderr (2
) 合并到 (>&
) 数据输出流(标准输出等效,1
- 见about_Redirection):
$combinedOutput = git fetch --all 2>&1
警告:在 7.1 之前的 PowerShell 版本中,如果$ErrorActionPreference = 'Stop'
恰好生效,使用2>
会意外触发终止错误;GitHub 问题 #4002 中讨论了此问题行为。
但是,其他 shell 的行为存在不明显的差异:
输出将是一个行数组,而不是单个多行字符串,
- 注意:从 PowerShell 7.2 开始 - 外部程序输出总是被解释为文本(字符串) - 不支持原始二进制输出;请参阅此答案。
正如预期的那样,源自stdout的行表示为字符串,但源自 stderr的行实际上是
[System.Management.Automation.ErrorRecord]
实例,尽管它们像字符串一样打印,并且在转换为字符串时确实会重现原始行,例如将结果发送到外部程序时.
此答案显示了如何按源流分隔捕获的行(假设stdout 和 stderr已合并)。能够在变量中单独捕获 stderr 输出是可取的,但是,PowerShell 7.2.x 不支持此功能。按照
2>variable:errs
的思路添加未来的支持是 GitHub 问题 #4332 的主题。
基于数组的结果对于解析是有利的;例如,找到包含单词unpacking
的行:
PS> $combinedOutput -match 'unpacking'
Unpacking objects: 100% (4/4), done.
注意:如果可能只输出了一行,请使用@($combinedOutput) -match 'unpacking'
如果您希望接收单个多行字符串:
$combinedOutput = (git fetch --all 2>&1) -join "`n" # n (LF); or: [Environment]::NewLine
如果你不介意尾随换行符作为字符串的一部分,你可以更简单地使用Out-String
:[2]
$combinedOutput = git fetch --all 2>&1 | Out-String
警告:在Windows PowerShell中,如果存在stderr行,则无法按预期工作,因为它们呈现为PowerShell错误记录(此问题已在PowerShell(Core)6+中修复);运行cmd /c 'echo data & echo err >&2' 2>&1 | Out-String
以查看问题。使用-join "`n"
解决方案来避免此问题。
注:
- 像往常一样,无论使用哪种重定向,确定外部程序调用是成功还是失败都应仅基于其退出代码,反映在 PowerShell 的自动
$LASTEXITCODE
变量中: 按照惯例(大多数,但不是所有程序都遵守),0
表示成功和任何非零值失败(一个值得注意的例外是robocopy
它使用多个非零退出代码来传达成功案例中的其他信息)。
[1] 有关在PowerShell中捕获外部程序输出的全面信息,请参阅此答案。
[2] 这种有问题的Out-String
行为在 GitHub 问题 #14444 中进行了讨论。
试试这个(没有IEX)
$x=git fetch --all