如何在不污染 PowerShell 错误输出的情况下重定向 stdout 和 stderr [英] How can I redirect stdout and stderr without polluting PowerShell error output

查看:25
本文介绍了如何在不污染 PowerShell 错误输出的情况下重定向 stdout 和 stderr的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过 PowerShell 运行命令并捕获它的 stdout 和 stderr 而不在屏幕上打印它们(命令非常嘈杂并污染控制台).

I am trying to run a command via PowerShell and capture its stdout and stderr without printing them on screen (command is incredibly noisy and pollutes the console).

我想在一个变量中捕获 stdout 和 stderr,然后在找到特定字符串时抛出异常.

I want to capture the stdout and stderr in a variable and then throw an exception if particular strings are found.

我的逻辑似乎在工作,我可以让 cmdlet 在我期望的时候失败/通过,但是输出与我期望的不匹配,而不是返回我指定的错误消息,我得到了我认为的结果来自命令的标准错误?

My logic seems to be working and I can make the cmdlet fail/pass when I expect it to, however the output does not match what I expect, instead of returning the error message that I am specifying I get what I believe is the stderr from the command instead?

(为了便于阅读而进行了简化)
第一个 cmdlet:

(Simplified for easier reading)
First cmdlet:

function Test-Validation
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 0)]
        [Array]
        $ValidExitCodes = @(0),

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)]
        [bool]
        $FailOnWarning = $true
    )
    $Validation = . pdk validate 2>&1

    if ($LASTEXITCODE -notin $ValidExitCodes)
    {
        throw "Module validation has failed. Exit code: $($LASTEXITCODE)."
    }
    $Warnings = $Validation -match 'pdk (WARNING)'
    if (($Warnings) -and ($FailOnWarning -eq $true))
    {
        throw "Module validation contains warnings.`n$Warnings"
    }
    Write-Verbose "Module successfully passed validation"
}

由第二个 cmdlet 调用:

Which is called by a second cmdlet:

function Test-Module
{
    [CmdletBinding()]
    param
    ()

    try
    {
      Test-Validation
    }
    catch
    {
      throw "$($_.Exception.Message)"
    }
    try
    {
      Test-Unit
    }
    catch
    {
      throw "$($_.Exception.Message)"
    }
}

预期输出

PS /opt/script/test/> Test-Module
Exception: /opt/script/test/Test-Module.ps1:13:9
Line |
  13 |          throw "$($_.Exception.Message)"
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Module validation has failed. Exit code: 1.

实际输出

PS /opt/script/test/> Test-Module
Exception: /opt/script/test/Test-Module.ps1:13:9
Line |
  13 |          throw "$($_.Exception.Message)"
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | pdk (INFO): Using Ruby 2.5.8 

如您所见,它似乎正在返回我正在运行的命令 (pdk) 的输出,而不是 模块验证失败.退出代码:$($LASTEXITCODE)." 我在测试验证 cmdlet 中定义的消息.

As you can see it seems to be returning the output from the command I'm running (pdk) instead of the "Module validation has failed. Exit code: $($LASTEXITCODE)." message I am defining in the Test-Validation cmdlet.

为什么我没有收到我想要的错误消息,有什么方法可以让我实现我想要的?除了任何代码建议外,我非常感谢有关我的问题的任何进一步阅读,以便我可以更好地理解这些内容.

Why am I not getting the error message I want and is there any way for me the achieve what I'm looking for? Alongside any code suggestions I would very much appreciate any further readings around my issues to so I can better understand these things.

注意:这是由 MacOS 上的 PoSh Core 运行

N.B.: This is being run by PoSh Core on MacOS

推荐答案

你的症状暗示 $ErrorActionPreference = 'Stop' 在时间函数中有效
Test-Validation 执行.(暂时)将其设置为 'Continue' 以解决您的问题 - 在 未来 版本中有望不再需要(见下文).

Your symptom implies that $ErrorActionPreference = 'Stop' is in effect at the time function
Test-Validation executes. (Temporarily) set it to 'Continue' to fix your problem - which in future versions will hopefully no longer required (see below).

观察到的行为的原因是,从 PowerShell 7.1 开始,使用错误流重定向(2>)使 PowerShell 路由成为 外部程序的stderr输出通过PowerShell的错误流(参见about_Redirection),并且 $ErrorActionPreference = 'Stop' 因此在第一个 stderr 时抛出脚本终止错误线路已收到.

The reason for the observed behavior is that, as of PowerShell 7.1, using an error-stream redirection (2>) makes PowerShell route an external program's stderr output through PowerShell's error stream (see about_Redirection), and $ErrorActionPreference = 'Stop' therefore throws a script-terminating error once the first stderr line is received.

这种行为很不幸,因为外部程序的 stderr 输出不能被假定为表示 错误 条件,因为外部程序实际上使用标准错误流 stderr 来处理任何除了数据,例如,包括状态信息.

This behavior is unfortunate, because stderr output from external programs cannot be assumed to represent an error condition, given that external programs in effect use stderr, the standard error stream, for anything that other than data, which includes status information, for instance.

PowerShell 7.2 及更高版本改进了这种行为:stderr 输出不再通过 PowerShell 的错误流进行路由,这意味着:

PowerShell 7.2 and above change this behavior for the better: stderr output is no longer routed through PowerShell's error stream, which means that:

偏好变量$ErrorActionPreference 不再对外部程序的 stderr 输出产生任何影响.

Preference variable $ErrorActionPreference no longer has any impact on stderr output from external programs.

automatic $? 变量,指示最近执行的语句的成功状态,不再错误地设置为$false 当进程退出代码为 0 and 时,也恰好有 stderr 输出 - 但请注意,您始终可以通过 自动 $LASTEXITCODE 变量

The automatic $? variable, which indicates the success status of the most recently executed statement, is no longer incorrectly set to $false when the process exit code is 0 and there also happens to be stderr output - though note that you can always infer success vs. failure of external programs via the automatic $LASTEXITCODE variable

这篇关于如何在不污染 PowerShell 错误输出的情况下重定向 stdout 和 stderr的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆