捕获程序的标准输出和标准错误并分别存储在变量中
捕获程序的标准输出和标准错误并分别存储在变量中
在一次运行中,是否可以将外部程序的stdout重定向到一个变量,将外部程序的stderr重定向到另一个变量?
例如:
$global:ERRORS = @(); $global:PROGERR = @(); function test() { # 在这里,我们能否将错误重定向到$PROGERR变量,将stdout保留给$OUTPUT变量? $OUTPUT = (& myprogram.exe 'argv[0]', 'argv[1]'); if ( $OUTPUT | select-string -Pattern "foo" ) { # 做一些操作 } else { $global:ERRORS += "test(): oh noes! 'foo' missing!"; } } test; if ( @($global:ERRORS).length -gt 0 ) { Write-Host "脚本特定错误发生"; foreach ( $err in $global:ERRORS ) { $host.ui.WriteErrorLine("err: $err"); } } else { Write-Host "脚本运行正常!"; } if ( @($global:PROGERR).length -gt 0 ) { # 做一些操作 } else { Write-Host "外部程序运行正常!"; }
一个乏味的例子,不过我想知道是否可以实现这个功能?
问题的原因是使用Start-Process命令时,没有正确设置-RedirectStandardError和-RedirectStandardOutput选项,导致无法将程序的标准输出和标准错误分别捕获到不同的变量中。解决方法是按照以下示例代码设置正确的选项和顺序:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "ping.exe" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = "localhost" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $stdout = $p.StandardOutput.ReadToEnd() $stderr = $p.StandardError.ReadToEnd() $p.WaitForExit() Write-Host "stdout: $stdout" Write-Host "stderr: $stderr" Write-Host "exit code: " + $p.ExitCode
此外,如果应用程序没有异步写入标准错误和标准输出,这段代码可能会导致死锁。为了解决这个问题,可以将WaitForExit()方法调用放在ReadToEnd()方法调用之后,即按照以下链接中所建议的方法进行调整:[http://stackoverflow.com/a/11549817/1127485](http://stackoverflow.com/a/11549817/1127485)
从上述内容中,我们可以总结出问题的原因是要将程序的stdout和stderr分别捕获到不同的变量中。而解决方法是使用一个文件来保存stderr的输出,并通过读取文件内容将其保存到变量中。
在给出的示例中,使用了一个名为"stderr.txt"的文件来保存stderr的输出。首先,通过运行命令& myprogram.exe 'argv[0]', 'argv[1]' 2>stderr.txt
来执行程序,并将stderr的输出重定向到"stderr.txt"文件中。接下来,通过get-content stderr.txt
命令将文件内容读取到变量"$err"中。最后,通过检查"$LastExitCode"的值,我们可以判断程序是否出现错误,并进行相应处理。
作者还建议使用"$LastExitCode"来检查从原生控制台EXE文件返回的错误。此外,作者还指出,对于stderr来说,输出的内容并不是命令本身产生的内容,而是主机对命令产生的内容的报告,这对于其他背景的用户可能会让人感到惊讶。
最后,作者提到了一个有用的工具New-TemporaryFile
,它可以方便地创建一个临时文件,这对于使用一次就不再需要的"stderr.txt"文件非常有用。
问题的原因是需要将程序的标准输出(stdout)和标准错误输出(stderr)分别捕获到不同的变量中。解决方法是通过将stdout和stderr合并成一个流,然后进行过滤。
首先,将程序的输出存储到一个变量中:
$allOutput = & myprogram.exe 2>&1
然后,通过筛选判断对象的类型,将stderr和stdout分别存储到不同的变量中:
$stderr = $allOutput | ?{ $_ -is [System.Management.Automation.ErrorRecord] } $stdout = $allOutput | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
更好的方法是使用管道符号和tee命令将输出打印到屏幕上,并将所有输出存储到一个变量中:
& myprogram.exe 2>&1 | tee -Variable allOutput
这样可以免费打印输出,并且保持stdout和stderr交错输出的顺序。这种方法也不需要通过文件,从而提高了性能并减少了可能出错的地方。
还可以结合捕获输出到变量中的方法,不输出到屏幕上:
[Void] (& myprog.exe 2>&1 | tee -Variable allOutput)
然后,通过判断对象的类型,将stdout存储到一个变量中:
$stdout = $allOutput | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
通过以上方法,可以将程序的stdout和stderr分别捕获到不同的变量中,方便后续的处理和分析。同时,通过tee命令将输出打印到屏幕上,保持输出顺序的一致性,提高了程序的性能和稳定性。