1

我们正在将我们的DevOps管道移动到一个新的集群中,并且在使用它时,我们在调用时遇到了一个奇怪的kind行为PowerShell。这也适用于kubectl

以下内容应仅作为重现,而不是真实世界的应用程序。换句话说,我不想修复以下代码,但我正在寻找错误发生原因的解释:

curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.10.0/kind-windows-amd64
Move-Item .\kind-windows-amd64.exe c:\temp\kind.exe -Force

$job = Start-Job -ScriptBlock { iex "$args" } -ArgumentList c:\temp\kind.exe, get, clusters
$job | Receive-Job -Wait -AutoRemoveJob

现在,如果我直接c:\temp\kind.exe get clusters在 PowerShell 窗口中执行命令,就不会发生错误:

在此处输入图像描述

换句话说,为什么 PowerShell(任何版本)将STDOUTofkind/kubectl视为STDERR?我怎样才能防止这种情况发生?

它必须有一个环境因素,因为相同的确切代码在一个系统中运行良好,而在另一个系统中它会引发错误......

4

1 回答 1

2

tl;博士

  • kind将其状态消息输出到stderr,后者在 PowerShell作业的上下文中通过 PowerShell 的错误输出流浮出水面,这使得它们以红色打印(并且容易受到$ErrorActionPreference = 'Stop'and的影响-ErrorAction Stop)。

  • 任何一个:

    • Silence stderr:2>$null用作一般机制,或者正如David Kruk建议的那样,使用特定于程序的选项来实现相同的效果,在kindis -q( --quiet)的情况下

    • 通过 PowerShell 的成功输出流重新路由标准错误输出,与标准输出输出合并,使用*>&1.

      • 警告:stdout 和 stderr 行之间的原始输出顺序不一定在输出上保持。
  • 另外,如果您想知道外部程序报告失败还是成功,您需要在作业的输出中包含自动$LASTEXITCODE变量的值,该变量包含最近执行的外部程序的进程退出代码(退出代码是唯一的可靠的成功/失败指示器 - 不是标准错误输出的存在或不存在)。

一个简化的例子*>&1(对于 Windows;在类 Unix 平台上,用cmdand/c替换shand -c):

$job = Start-Job -ScriptBlock {
  param($exe)
  & $exe $args *>&1
  $LASTEXITCODE  # Also output the process exit code.
} -ArgumentList cmd, /c, 'echo data1; echo status >&2; echo data2'
$job | Receive-Job -Wait -AutoRemoveJob

正如许多实用程序所做的那样,kind显然是通过 stderr报告状态消息。

  • 鉴于 stdout 用于data,因此将唯一可用的其他输出流 stderr 用于任何不是 data的内容,以防止数据输出受到污染。结果是 stderr 输出不一定表示实际错误(成功与失败只能从外部程序的进程退出代码中推断出来)。

  • PowerShell(仅用于它自己的命令)值得称道的是具有更多样化的输出流系统,在概念性about_Redirection帮助主题中有记录,例如,允许您通过 报告状态消息Write-Verbose

PowerShell 将外部程序的输出流映射到它自己的流,如下所示:

  • 标准输出:

    • Stdout 输出映射到 PowerShell 的成功输出流(带有 number 的流1,类似于 stdout 在cmd.exe和 POSIX 兼容的 shell 中的引用方式),允许在变量 ( $output = ...) 中捕获它或重定向到文件 ( > output.txt) 或发送通过管道到另一个命令。
  • 标准错误输出:

    • 在控制台(终端)中的本地前台处理中,stderr 默认情况下根本不映射,而是传递到显示器不是红色) - 除非使用2>重定向,这允许您抑制 stderr 输出(2>$null)或将其发送到文件 ( 2>errs.txt)

      • 这是适当的,因为 PowerShell 不能也不应该假设 stderr 输出代表实际错误,而 PowerShell 的错误流旨在专门用于错误。
    • 不幸的是,从 PowerShell 7.2 开始,在 PowerShell作业(使用Start-Jobor创建Start-ThreadJob)和远程处理(例如,在调用中)的上下文中,stderr 输出映射到 PowerShell 的错误流Invoke-Command -ComputerName ...(带有 number 的流2,类似于在cmd.exe和 POSIX 兼容的外壳)。

      • 警告:这意味着如果$ErrorActionPreference = 'Stop'有效或-ErrorAction Stop传递给Receive-JobInvoke-Command,例如,来自外部程序的任何 stderr 输出将触发脚本终止错误 - 即使 stderr 输出仅包含状态消息。由于 PowerShell 7.1 及以下版本中的错误,如果使用2>重定向,这也可能发生在本地前台调用中。

结果:_

  • 要使stderr 输出静音2>$null请在(作业或远程命令内部)或接收端应用。

  • 要通过成功输出流/stdout路由 stderr 输出(所有流),即合并所有流,请使用*>&1

    • 为了防止 stderr 行以红色打印(当来自作业或远程命令时),请在源处应用此重定向- 这也可以防止来自调用方$ErrorActionPreference = 'Stop'/的副作用。-ErrorAction Stop

    • 注意:如果将所有流与 合并*>&1,则不能保证 stdout 和 stderr 行的输出顺序反映原始输出顺序,从 PowerShell 7.2 开始。

    • 如果需要,PowerShell 仍然允许您稍后根据它们是来自 stdout 还是 stderr 来分隔输出行 - 请参阅此答案

于 2021-08-27T19:50:27.357 回答