Invoke-Expression,不是所有的输出都返回给变量

Invoke-Expression, not all output returned to variable

我在我的 PowerShell 脚本中使用了一些 GIT 命令。大多数时候我通过 Invoke-Expression 调用 GIT 命令,这样我,例如

在某些 GIT 命令中,我认识到并非所有输出都是 return 通过 Invoke-Expression 编辑的,尽管 documentation 声明:

Outputs

PSObject

Returns the output that is generated by the invoked command (the value of the Command parameter).

这是一个例子:

> $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

因此主要信息未return编辑为$x。我无法想象 git fetch --all 是 return 通过 stderr 获取主要信息(没有意义......)。

我也找到了这个 PowerShell question,没有答案,使用的 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 到 return 整个输出?

感谢

试试这个(没有 iex)

$x=git fetch --all

正如我在“", with 中提到的,您可以先尝试设置:

set GIT_REDIRECT_STDERR=2>&1

然后在您的 Powershell 脚本中,您应该同时获得 stdout 和 stderr 输出,

另请参阅 dahlbyk/posh-git issue 109 以获取更类似于 Powershell 的示例:

$env:GIT_REDIRECT_STDERR = '2>&1'

特别适用于 git,但值得讨论 通用解决方案:


注意:Invoke-Expression should generally be avoided并且没有理由使用它来调用外部程序:直接调用它们并赋值给一个变量:

$capturedStdout = git ... # capture git's stdout output as an array of lines

如前所述,gitstatus 信息输出到 stderr ,而 data 转到标准输出; 一个PowerShell变量赋值只捕获stdout输出.[1]

要捕获 stdout 和 stderr组合,交错,因为它会打印到终端,您可以使用重定向 2>&1,就像在其他 shell 中一样,将错误流/stderr (2) 合并到 (>&) 数据输出流中(stdout 等效,1 - 参见 about_Redirection):

$combinedOutput = git fetch --all 2>&1 

警告:在 v7.1 之前的 PowerShell 版本中,如果 $ErrorActionPreference = 'Stop' 恰好生效,则使用 2> 意外触发终止错误; GitHub issue #4002.

中讨论了这种有问题的行为

与其他 shell 的行为存在不明显的差异,但是:

  • 输出将是一个行数组,而不是单个多行字符串,

    • 注意:从 PowerShell 7.2 开始 - 外部程序输出 总是 解释为 text(字符串) - 不支持原始二进制输出;见 .
  • 源自 stdout 的行按预期表示为 strings,但源自 [=77 的行=]stderr 实际上是 [System.Management.Automation.ErrorRecord] 个实例,虽然它们 print 像字符串并且在 conversion 上重现了字符串原始行,例如将结果发送到外部程序时。
    展示了如何通过源流分离捕获的行(假设合并了 stdout 和 stderr)。

基于数组的结果有利于解析;例如,查找包含单词 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 行是present,因为它们呈现为 PowerShell error records(此问题已在 PowerShell (Core) 6+ 中修复); 运行 cmd /c 'echo data & echo err >&2' 2>&1 | Out-String 看问题。使用 -join "`n" 解决方案来避免这个问题。


注:

  • 像往常一样,无论您使用什么重定向,确定外部程序调用成功还是失败应该仅基于 关于其 退出代码 ,反映在 PowerShell 的 automatic $LASTEXITCODE variable 中:按照惯例(大多数但不是所有程序都遵守),0 表示 success 和任何 nonzero 值失败(一个值得注意的例外是 robocopy 它使用几个非零退出代码来传达附加信息成功 案例)。

[1] 有关在 PowerShell 中捕获外部程序输出的综合信息,请参阅 this answer

[2] 这种有问题的 Out-String 行为在 GitHub issue #14444.

中进行了讨论