Capture program stdout and stderr to separate variables
Solution 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.
Solution 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] }
Solution 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
Solution 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.
Solution 5
At first, this answer was published under another question.
The two questions are similar, but the attention here is higher, so I'll send another one here, which may provide solutions for more people.
Using Where-Object
(The alias is symbol ?
) is an obvious method, but it's a bit too cumbersome. It needs a lot of code.
In this way, it will not only take longer time, but also increase the probability of error.
In fact, there is a more concise method that separate different streams to different variable in PowerShell(it came to me by accident).
# First, declare a method that outputs both streams at the same time.
function thisFunc {
[cmdletbinding()]
param()
Write-Output 'Output'
Write-Verbose 'Verbose'
}
# The separation is done in a single statement.Our goal has been achieved.
$VerboseStream = (thisFunc -Verbose | Tee-Object -Variable 'String' | Out-Null) 4>&1
Then we verify the contents of these two variables
$VerboseStream.getType().FullName
$String.getType().FullName
The following information should appear on the console:
PS> System.Management.Automation.VerboseRecord
System.String
'4>&1' means to redirect the verboseStream to the success stream, which can then be saved to a variable, of course you can change this number to any number between 2 and 5.
If you feel and my method is not bad, please click the mouse to vote for me, thank you very much.
Related videos on Youtube
dusz
Updated on July 09, 2022Comments
-
dusz almost 2 years
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?
-
Alexander Obersht almost 10 yearsYou could use Start-Process to run myprogram.exe as described here. It captures STDOUT and STDERR separately.
-
-
johnnycrash over 9 yearsThis can deadlock an app that doesn't write asynchronously to stderr and stdout.
-
sschuberth over 8 years@johnnycrash Any suggestion how to fix that?
-
sschuberth over 8 years
-
Ohad Schneider about 8 yearsOr even better, replace the first line with
& myprogram.exe 2>&1 | tee -Variable allOutput
. That way you get the output printed for free, even keeping the order when stdout and stderr are interleaved (none of the other answers give that). This also doesn't go through any files which is a win in terms of performance and minimization of things that can fail. -
Cameron Kerr over 7 yearsIt is important to point out (at least for stderr), the output is not what the command produces, but rather it is the hosts report of what the command-produced, which would come as a shock to those coming from other backgrounds.
-
ComFreek over 6 yearsCombining @OhadSchneider's approach with capturing the output in a variable without outputting it:
[Void] (& myprog.exe 2>&1 | tee -Variable allOutput)
and then$stdout = $allOutput | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
. -
Franklin Yu about 6 yearsSince
stderr.txt
is probably used only once,New-TemporaryFile
comes in handy