Capture program stdout and stderr to separate variables

不问归期 提交于 2019-11-26 08:23:52

问题


Is it possible to redirect stdout from an external program to a variable and stderr from external programs to another variable in one run?

For example:

$global:ERRORS = @();
$global:PROGERR = @();

function test() {
    # Can we redirect errors to $PROGERR here, leaving stdout for $OUTPUT?
    $OUTPUT = (& myprogram.exe \'argv[0]\', \'argv[1]\');

    if ( $OUTPUT | select-string -Pattern \"foo\" ) {
        # do stuff
    } else {
        $global:ERRORS += \"test(): oh noes! \'foo\' missing!\";
    }
}

test;
if ( @($global:ERRORS).length -gt 0 ) {
    Write-Host \"Script specific error occurred\";
    foreach ( $err in $global:ERRORS ) {
        $host.ui.WriteErrorLine(\"err: $err\");
    }
} else {
    Write-Host \"Script ran fine!\";
}

if ( @($global:PROGERR).length -gt 0 ) {
    # do stuff
} else {
    Write-Host \"External program ran fine!\";
}

A dull example however I am wondering if that is possible?


回答1:


The easiest way to do this is to use a file for the stderr output, e.g.:

$output = & myprogram.exe 'argv[0]', 'argv[1]' 2>stderr.txt
$err = get-content stderr.txt
if ($LastExitCode -ne 0) { ... handle error ... }

I would also use $LastExitCode to check for errors from native console EXE files.




回答2:


One option is to combine the output of stdout and stderr into a single stream, then filter.

Data from stdout will be strings, while stderr produces System.Management.Automation.ErrorRecord objects.

$allOutput = & myprogram.exe 2>&1
$stderr = $allOutput | ?{ $_ -is [System.Management.Automation.ErrorRecord] }
$stdout = $allOutput | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }



回答3:


You should be using Start-Process with -RedirectStandardError -RedirectStandardOutput options. This other post has a great example of how to do this (sampled from that post below):

$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
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode



回答4:


This is also an alternative that I have used to redirect stdout and stderr of a command line while still showing the output during PowerShell execution:

$command = "myexecutable.exe my command line params"

Invoke-Expression $command -OutVariable output -ErrorVariable errors
Write-Host "STDOUT"
Write-Host $output
Write-Host "STDERR"
Write-Host $errors

It is just another possibility to supplement what was already given.

Keep in mind this may not always work depending upon how the script is invoked. I have had problems with -OutVariable and -ErrorVariable when invoked from a standard command line rather than a PowerShell command line like this:

PowerShell -File ".\FileName.ps1"

An alternative that seems to work under most circumstances is this:

$stdOutAndError = Invoke-Expression "$command 2>&1"

Unfortunately, you will lose output to the command line during execution of the script and would have to Write-Host $stdOutAndError after the command returns to make it "a part of the record" (like a part of a Jenkins batch file run). And unfortunately it doesn't separate stdout and stderr.



来源:https://stackoverflow.com/questions/24222088/capture-program-stdout-and-stderr-to-separate-variables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!