When an external command, such as git, writes to stderr, PowerShell generates a NativeCommandError exception. I would like to see the output normally along with stdout similar to how it might look on a standard UNIX/Linux system. There are a number of native commands that this script needs to run and I would prefer a solution that does not add too much clutter and maintenance to each command, if possible.
On Linux, a branch can be checked out as such:
$ git checkout devel
Branch 'devel' set up to track remote branch 'devel' from 'origin'.
Switched to a new branch 'devel'
The subtle part of that is that that last line was written to stderr for whatever reason. However, the exit status of git was zero indicating it was successful. Under PowerShell, I get this:
PS> git checkout devel
Branch 'devel' set up to track remote branch 'devel' from 'origin'.
git : Switched to a new branch 'devel'
At line:1 char:1
+ git checkout devel
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Switched to a new branch 'devel':String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Checking $LASTEXITSTATUS
, it shows as zero indicating that the checkout was successful. However, because git's behavior is to write to stderr certain messages, it triggers PowerShell to register an error. I can just hide the message and check for the exit status manually, but I don't want to hide actual error messages when they show. This command does avoid the exception:
PS> git checkout devel 2>$null
Branch 'devel' set up to track remote branch 'devel' from 'origin'.
And is what other answers on StackOverflow have suggested, but isn't what I need. I've tried redirecting stderr to stdout with hope that PowerShell would see all output on stdout and not trigger an exception, but it still does:
git checkout devel 2>&1
Branch 'devel' set up to track remote branch 'devel' from 'origin'.
git : Switched to a new branch 'devel'
At line:1 char:1
+ git checkout devel 2>&1
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Switched to a new branch 'devel':String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
I have also tried catching the exception, but the try..catch block does not seem to block it:
PS> Try { git checkout devel 2>&1 } catch { echo "There was an error, ignore" }
Branch 'devel' set up to track remote branch 'devel' from 'origin'.
git : Switched to a new branch 'devel'
At line:1 char:7
+ Try { git checkout devel 2>&1 } catch { echo "There was an error, ign ...
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Switched to a new branch 'devel':String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
I only want a native command to cause a failure if it had a non-zero exit status, not if it only writes to stderr. How do I prevent native commands in a PowerShell script from throwing an exception on writes to stderr without completely blocking stderr?
tl;dr
The simplest workaround for the PowerShell ISE is the following (not needed in a regular console or in Visual Studio Code):
# Redirect stderr (2) to the success stream (1).
# Doing so wraps stderr lines in [System.Management.Automation.ErrorRecord]
# instances; calling .ToString() on them returns them as normal text.
# CAVEAT: BE SURE THAT $ErrorActionPreference = 'Stop' is NOT IN EFFECT.
git checkout devel 2>&1 | % ToString
For other workarounds, including the ability to distinguish lines by stream of origin, see this answer.
However, you're better off switching to Visual Studio Code as your development environment, which solves the problem fundamentally - see below.
Two asides:
What you're seeing are non-terminating errors, not exceptions (while .NET exceptions underly PowerShell's error handling, they are always wrapped in [System.Management.Automation.ErrorRecord]
instances and may or may not abort execution (terminating vs. non-terminating error)).
try
/ catch
doesn't catch non-terminating errors, only terminating ones; while you could set $ErrorActionPreference = 'Stop'
beforehand to promote non-terminating errors to terminating ones, execution of your program (in the ISE ) would be aborted on receiving the first stderr line.
You can avoid this problem if you limit your PowerShell use to:
a terminal (console), including the upcoming Windows Terminal
Visual Studio Code with the PowerShell extension; this cross-platform editor (IDE) is meant to supersede the obsolescent Windows PowerShell ISE.
These environments behaves as you would expect:
Note:
In the context of remoting and background jobs, stderr output is still sent to PowerShell's error stream.
There are additional, host-independent problems with the use of 2>
with external programs.
See this answer for a comprehensive overview of all problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With