【问题标题】:Why does PowerShell interpret kind/kubectl STDOUT as STDERR and How to Prevent it?为什么 PowerShell 将 kind/kubectl STDOUT 解释为 STDERR 以及如何防止它?
【发布时间】:2021-07-17 18:08:02
【问题描述】:

我们正在将我们的 DevOps 管道移动到一个新的集群中,在此期间,我们在使用 PowerShell 调用 kind 时遇到了一个奇怪的行为。这也适用于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

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

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

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

【问题讨论】:

  • 来自kind.exeNo kind clusters found 在两个描述的调用中都在STDERR 中。尝试以2>NUL c:\temp\kind.exe get clusters 的身份从打开的cmd.exe 窗口 运行后者。它只是没有颜色......
  • @JosefZ 我不明白。为什么kind 产生STDERR 几乎任何东西?我的意思是,当我创建带有消息 Creating cluster "kind" ... 的集群以及删除带有错误消息 Deleting cluster "kind" ... 的集群时,我会收到来自 PowerShell 的红色错误消息和堆栈跟踪。当然这些应该是STDOUT,不是吗?每次打电话给kind时,我真的需要2>&1吗?
  • 我认为这符合预期,因为kind 将在STDERR 上输出所有内容。您是否考虑过忽略这些消息并使用-q/--quiet - silence all stderr output 运行kind?我认为您也可以在kind github 页面上获取更多参考:github.com/kubernetes-sigs/kind
  • @DawidKruk 好吧,我已经编写了一个 PowerShell 模块来处理这种情况。总而言之,PowerShell 错误处理似乎有点混乱:github.com/MicrosoftDocs/PowerShell-Docs/issues/1583
  • @JaniHyytiäinen 很高兴您找到了解决方案。我鼓励您使用您创建的模块发布答案,以便社区将来可以从中受益。

标签: powershell stdout kubectl stderr kind


【解决方案1】:

tl;dr

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

  • 要么:

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

    • 重新路由stderr输出通过PowerShell的成功输出流,与stdout输出合并,使用*>&1

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

*>&1 的简化示例(对于 Windows;在类 Unix 平台上,将 cmd/c 替换为 sh-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 显然通过标准错误报告状态消息。

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

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

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

  • 标准输出输出:

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

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

      • 这是适当的,因为 PowerShell 不能也不应该假定 stderr 输出代表实际的错误,而 PowerShell 的错误流旨在专门用于错误。李>

    • 不幸的是,从 PowerShell 7.2 开始,在 PowerShell jobs(使用 Start-JobStart-ThreadJob 创建)和 remoting(例如,在 Invoke-Command -ComputerName ... 调用中)的上下文中,stderr输出映射到 PowerShell 的错误流(编号为 2 的流,类似于在 cmd.exe 和 POSIX 兼容的 shell 中如何引用标准输出)。

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

结果

  • 为了 silence stderr 输出,应用2>$null - 在(在作业或远程命令内部),或在接收端

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

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

    • 注意:如果您使用*>&1 合并所有流,则输出 stdout 和 stderr 行的顺序保证反映原始输出顺序,如PowerShell 7.2。

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

【讨论】:

    猜你喜欢
    • 2017-10-28
    • 2021-12-02
    • 2022-01-18
    • 2014-07-17
    • 2021-03-07
    相关资源
    最近更新 更多