【问题标题】:Invoke-Expression, not all output returned to variableInvoke-Expression,并非所有输出都返回到变量
【发布时间】:2019-12-30 01:13:17
【问题描述】:

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

  • 可以解析输出,或/和
  • 将输出转发到日志记录方法。

在某些 GIT 命令中,我发现并非所有输出都通过 Invoke-Expression 返回,尽管 documentation 声明:

输出

PS 对象

返回调用命令生成的输出(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 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 返回整个输出?

谢谢

【问题讨论】:

    标签: git powershell io-redirection stderr powershell-6.0


    【解决方案1】:

    试试这个(没有iex)

    $x=git fetch --all
    

    【讨论】:

    • 建议直接调用而不是毫无意义地使用 Invoke-Expression 是个好主意,但 OP 的主要问题是无法同时捕获 stderr 输出。
    【解决方案2】:

    正如我在“PowerShell Capture Git Output”中提到的,用Git 2.16 (Q1 2018),你可以先尝试设置:

    set GIT_REDIRECT_STDERR=2>&1
    

    然后在你的 Powershell 脚本中,你应该得到 stdout 和 stderr 的输出,

    另请参阅dahlbyk/posh-git issue 109 了解更多类似 Powershell 的示例:

    $env:GIT_REDIRECT_STDERR = '2>&1'
    

    【讨论】:

      【解决方案3】:

      VonC's answer 特别适用于 git,但值得讨论通用解决方案


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

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

      如前所述,gitstatus 信息输出到 stderr,而 data 输出到 stdout ; 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(字符串) - 不支持原始二进制输出;见this answer
      • 源自 stdout 的行表示为 strings,正如预期的那样,但源自 stderr 的行实际上是 [System.Management.Automation.ErrorRecord] 实例,尽管它们print 像字符串一样,并且在conversion 到字符串时会重现原始行,例如在将结果发送到外部程序时。
        This answer 显示如何分离按源流捕获的行(假设 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 行,这将无法按预期工作,因为它们像 PowerShell 一样呈现 错误记录(此问题已在 PowerShell (Core) 6+ 中修复);运行cmd /c 'echo data & echo err >&2' 2>&1 | Out-String 来查看问题。使用-join "`n" 解决方案来避免问题。


      注意:

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

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

      [2] 这种有问题的Out-String 行为在GitHub issue #14444 中进行了讨论。

      【讨论】:

        猜你喜欢
        • 2021-12-11
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 2021-01-04
        • 2011-01-11
        • 1970-01-01
        • 2014-02-07
        • 1970-01-01
        相关资源
        最近更新 更多