执行命令时如何忽略PowerShell中的特定错误?

g.p*_*dou 5 error-handling powershell powershell-3.0

我知道ErrorAction参数,还有$ErrorActionPreference$Error$

语境

我想解决的问题是,在运行外部命令(例如 choco 或 git)时,它会给出错误,至少在我的任务上下文中应该是警告或什至不警告。

由于它们的退出代码,PowerShell 认为该结果在任何意义上都是错误,例如将红色写入输出等,这在我的任务上下文中是不可取的。

我可以使用-ErrorAction或来抑制这些命令的错误输出2> $null,但是我对以这种方式完全消除特定命令的任何错误有一种不好的感觉。

我只想忽略已知的“在这种情况下对我来说不是问题”类型错误。

有没有办法处理命令的错误,忽略一些特定的,但正常对待所有其他错误条件?

mkl*_*nt0 5

  • 在常规控制台窗口中,在 PowerShell v3 及更高版本中, stderr 行的打印就像 stdout 行一样

    • 这是可取的,因为许多程序使用 stderr 不仅报告真正的错误,还报告任何非数据的内容,例如状态消息

    • Stderr 行(除非重定向 - 见下文)直接通过控制台打印(而 stdout 行被发送到 PowerShell 的成功输出流,在那里它们可以收集在一个变量中,通过管道发送,或使用>/重定向>>)。

  • 令人遗憾的是,PowerShell的ISE,即使是在V5.1打印标准错误线红色,在相同的格式PowerShell的错误

    • 带有 PowerShell 扩展的Visual Studio Code没有这个问题,一般来说值得迁移,因为所有未来的开发工作都将集中在那里,并且它也适用于跨平台的 PowerShell Core版本。
  • 表现良好的控制台应用程序使用其退出代码来表示成功(退出代码0)与失败(任何非零退出代码)。PowerShell 将最近调用的控制台应用程序的退出代码保存在其自动$LASTEXITCODE变量中

  • 作为旁白:

    • 通用参数-ErrorAction-ErrorVariable 不能与外部程序一起使用。
    • 同样,首选项变量$ErrorActionPreference没有影响(除了意外,由于这个错误,从 PowerShell v5.1 / PowerShell Core 6.2.0-preview.4 开始)。
    • try/catch不能用于检测和处理外部程序的故障。
    • 有关 PowerShell 复杂错误处理规则的全面概述,请参阅此 GitHub 文档问题

注意:我在下面的示例中使用以下外部命令,它们生成 1 行 stdout 和 1 行 stderr 输出(请注意,重定向>&2由 处理cmd,而不是 PowerShell,因为它在 内部'...';它用于生成标准错误输出):

cmd /c 'echo stdout & echo stderr >&2' 
Run Code Online (Sandbox Code Playgroud)

通过非零退出代码 ( )使命令信号失败的变体exit 1

cmd /c 'echo stdout & echo stderr >&2 & exit 1' 
Run Code Online (Sandbox Code Playgroud)

因此,通常以下内容就足够了

$stdOut = cmd /c 'echo stdout & echo stderr >&2' # std*err* will print to console
if ($LASTEXITCODE -ne 0) { Throw "cmd failed." } # handle error
Run Code Online (Sandbox Code Playgroud)

这会捕获变量中的stdout输出$stdout并将stderr输出传递到控制台。


如果您想稍后收集 stderr 输出并且仅在出现错误时显示它们

$stdErr = @() # initialize array for collecting stderr lines.

# Capture stdout output while collecting stderr output in $stdErr.
$stdOut = cmd /c 'echo stdout & echo stderr >&2 & exit 1' 2>&1  | Where-Object { 
  $fromStdErr = $_ -is [System.Management.Automation.ErrorRecord]
  if ($fromStdErr) { $stdErr += $_.ToString() }
  -not $fromStdErr # only output line if it came from stdout
}

# Throw an error, with the collected stderr lines included.
if ($LASTEXITCODE -ne 0) { 
  Throw "cmd failed with the following message(s): $stdErr"
}
Run Code Online (Sandbox Code Playgroud)
  • 请注意2>&1重定向,它也指示 PowerShell2通过管道(流1,成功流)发送 stderr 行(流,错误流)。

  • Where-Object块中,$_ -is [System.Management.Automation.ErrorRecord]用于标识stderr行,因为 PowerShell 将此类行包装在该类型的实例中。

或者,您可以在临时文件( 2>/path/to/tmpfile) 中收集 stderr 输出,读取其内容,然后将其删除。

这个 GitHub 问题建议引入在变量中收集重定向 stderr 输出的选项,类似于您可以如何要求cmdlet通过公共参数收集变量中的错误
-ErrorVariable

有两个警告

  • 本质上,通过稍后仅打印 stderr 行,可能会丢失与stdout输出相关的特定上下文。

  • 由于一个错误的的Windows PowerShell V5.1 / PowerShell核心6.2.0的$ErrorActionPreference = Stop 绝不能生效,因为2>&1重定向然后触发一个脚本终止错误如收到的第一个标准错误行尽快。


如果您想在收到stderr 行时有选择地对其进行操作

笔记:

  • 本质上,当您处理这些行时,您还不知道程序最终会报告失败还是成功。

  • $ErrorActionPreference = 'Stop'原则同样适用这里。

以下示例过滤掉 stderr 行stderr1并将行stderr2以红色打印到控制台(仅文本,不像 PowerShell 错误)。

$stdOut = cmd /c 'echo stdout & echo stderr1 >&2 & echo stderr2 >&2' 2>&1 |
  ForEach-Object { 
    if ($_ -is [System.Management.Automation.ErrorRecord]) { # stderr line
      $stdErrLine = $_.ToString()
      switch -regex ($stdErrLine) {
        'stderr1' { break } # ignore, if line contains 'stderr1'
        default   { Write-Host -ForegroundColor Red $stdErrLine }
      }
    } else { # stdout line
      $_ # pass through
    }
  }

# Handle error.
if ($LASTEXITCODE -ne 0) { Throw "cmd failed." }
Run Code Online (Sandbox Code Playgroud)