I need to capture the output of a external process into a variable (string), so I can do some processing on that.
The answer from here works nicely as long as the process writes to stdout. However, if the process fails, it writes to stderr. I'd like to capture this string too, and I can't figure out how to do it.
Example:
$cmdOutput = (svn info) | out-string
This works, unless SVN has an error. If an error occured, SVN writes to stderr, and $cmdOutput
is empty.
How do I capture the text written to stderr in a variable in PowerShell ?
To complement manojlds' helpful answer with an overview:
To briefly explain redirection expression
2>&1
:2>
redirects (>
) PowerShell's error output stream, whose number is2
(and which maps onto stderr) into (&
) PowerShell's success output stream, whose number is1
(and maps onto stdout).Run
Get-Help about_Redirection
to learn more.Capturing stdout and stderr output combined, as a merged stream:
As a collection of output lines as strings, without the ability to tell which line came from what stream:
Using the platform-native shell to perform the merging at the source makes PowerShell only see stdout output, which, as usual, it collects in an array of strings.
In the following examples, the commands each create both stdout and stderr output.
All commands use PSv3+ syntax for convenience and brevity.
Windows example:
Unix example (PowerShell Core):
Note:
You do need to call the platform-native shell explicitly (
cmd /c
on Windows,sh -c
on Unix), which makes this approach less portable.You will not be able to tell from the resulting array of lines which line came from what stream.
See below for how to make this distinction.
As a mix of string lines (from stdout) and
[System.Management.Automation.ErrorRecord]
"lines" (from stderr):By using PowerShell's
2>&1
redirection, you also get a single collection of lines representing the merged stdout and stderr streams, but the stderr lines aren't captured as strings, but as[System.Management.Automation.ErrorRecord]
instances.Caveat: A bug as of Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.4 results in unexpected behavior when you redirect PowerShell's error stream with
2>
while$ErrorActionPreference = 'Stop'
is in effect - see this GitHub issue.This gives you the flexibility to distinguish between stdout and stderr lines by examining the data type of each array element.
On the flip side, you may have to convert the stderr lines to strings.
In PSv6, the different data types are not obvious when you simply output the captured output, but you can tell by reflection:
Inspect the result:
As you can see, the last array element is an error record, which represent the single
File Not Found
stderr output line produced by thedir \nosuch
command.Note: Up to PSv5.1, when you output the captured output to the console, the
[System.Management.Automation.ErrorRecord]
instances actually rendered in the same format as PowerShell errors, perhaps making it appear as if an error had occurred then.In PSv6, these error records print just their message, i.e., the contents of the original stderr line, and visually you can't tell that an error record rather than a string is being printed.
To convert all captured output to strings:
To filter out the stderr lines (and converting them to strings in the process):
Capturing stderr output separately:
Using a temporary file:
As of PSv5.1, the only direct way to capture stderr output in isolation is to use redirection
2>
with a filename target; i.e., to capture stderr output - as text - in a file:Clearly, this is cumbersome and also slower than in-memory operations.
Using the PSv4+
.Where()
collection operator:The (little-known) PSv4+
.Where()
collection operator allows you to split a collection in two, based on whether the elements pass a Boolean test or not:Potential future alternative:
This GitHub issue proposes a new redirection syntax that would allow collecting stderr lines in a variable much more simply:
Try this: