我希望能够在PowerShell中捕获一些流式输出。例如:
cmd /c "echo hi && foo"
这个命令应该会打印“hi”,然后是“bomb”。我知道可以使用-ErrorVariable:
Invoke-Command { cmd /c "echo hi && foo" } -ErrorVariable ev
然而存在一个问题:对于长时间运行的命令,我希望能够流式传输输出,而不是捕获并仅在命令结束时获取stderr/stdout输出。
理想情况下,我希望能够将stderr和stdout分开并连接到两个不同的流中,并将stdout管道返回给调用者,但准备在发生错误时丢弃stderr。类似以下内容:
$stdErr
Invoke-Command "cmd" "/c `"echo hi && foo`"" `
-OutStream (Get-Command Write-Output) `
-ErrorAction {
$stdErr += "`n$_"
Write-Error $_
}
if ($lastexitcode -ne 0) { throw $stdErr}
我能做的最接近的方法是使用管道,但这不能让我区分标准输出和标准错误输出,因此我最终只能抛弃整个输出流
function Invoke-Cmd {
<#
.SYNOPSIS
Executes a command using cmd /c, throws on errors.
#>
param([string]$Cmd)
)
$out = New-Object System.Text.StringBuilder
# I need the 2>&1 to capture stderr at all
cmd /c $Cmd '2>&1' |% {
$out.AppendLine($_) | Out-Null
$_
}
if ($lastexitcode -ne 0) {
# I really just want to include the error stream here
throw "An error occurred running the command:`n$($out.ToString())"
}
}
常见用法:
Invoke-Cmd "GitVersion.exe" | ConvertFrom-Json
请注意,只使用ScriptBlock(并检查输出流中的[ErrorRecord]是不可接受的,因为有许多程序“不喜欢”直接从PowerShell进程执行。.NET System.Diagnostics.Process API可以让我做到这一点...但是,我无法从流处理程序内部流式传输输出(由于线程和阻塞-尽管我想我可以使用while循环,并在数据进入时流/清除收集的输出)。
2>&1
重定向并过滤类型为[System.Management.Automation.ErrorRecord]
的部分很有趣。可惜它非常依赖于重定向发生的位置。这使得向人们解释变得困难,但你已经做得很好了。 - Burt_Harris