如何在不污染PowerShell错误输出的情况下重定向标准输出和错误输出。
如何在不污染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运行的。
你的症状表明在函数`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)推断出外部程序的成功与失败。