Capture program stdout and stderr to separate variables

dusz picture dusz · Jun 14, 2014 · Viewed 44.8k times · Source

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?

Answer

Aaron Schultz picture Aaron Schultz · Oct 7, 2015

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] }