如何在不污染PowerShell错误输出的情况下重定向标准输出和错误输出。

24 浏览
0 Comments

如何在不污染PowerShell错误输出的情况下重定向标准输出和错误输出。

问题:

我试图通过PowerShell运行一个命令,并捕获其stdout和stderr,而不将它们打印在屏幕上(该命令非常嘈杂,会污染控制台)。

我想将stdout和stderr捕获到一个变量中,然后如果找到特定的字符串,就抛出一个异常。

我的逻辑似乎是有效的,我可以在期望的时候使命令失败/通过,但输出与我期望的不符,我得到的是我认为是命令的stderr?

我的代码:

(为了更容易阅读,进行了简化)

第一个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调用:

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)的输出,而不是我在Test-Validation cmdlet中定义的"Module validation has failed. Exit code: $($LASTEXITCODE)."消息。

为什么我没有得到我想要的错误消息,是否有任何方法可以实现我想要的效果?

除了任何关于我的问题的代码建议,我非常乐意阅读更多的内容,以便更好地理解这些问题。

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

0
0 Comments

你的症状表明在函数`Test-Validation`执行时,`$ErrorActionPreference = 'Stop'`已生效。将其临时设置为`'Continue'`即可解决问题,但在将来的版本中希望不再需要这样做(详见下文)。

观察到这种行为的原因是,在Windows PowerShell和PowerShell(Core)7.1及以下版本中,使用错误流重定向(`2>`)会导致PowerShell通过其错误流路由外部程序的stderr输出(详见[about_Redirection](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Redirection)),因此,一旦接收到第一行stderr,`$ErrorActionPreference = 'Stop'`就会引发脚本终止错误。

这种行为是不幸的,因为不能假设外部程序的stderr输出代表一个错误条件,因为外部程序实际上使用stderr(标准错误流)除了数据之外的任何内容,包括状态信息等。

PowerShell 7.2及以上版本改变了这种行为,使其变得更好:stderr输出不再通过PowerShell的错误流路由,这意味着:

-(幸运的是)stderr行不再收集在[automatic `$Error` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#error)中。

- [Preference variable `$ErrorActionPreference`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Preference_Variables#erroractionpreference)不再影响外部程序的stderr输出。

- [automatic `$?` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#section-1),它指示最近执行的语句的成功状态,当进程退出码为0且存在stderr输出时不再错误地设置为`$false` - 但请注意,您始终可以通过[automatic `$LASTEXITCODE` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#lastexitcode)推断出外部程序的成功与失败。

0