$LastExitCode=0, but $?=False in PowerShell. Redirecting stderr to stdout gives NativeCommandError

command-line, powershell, windows

Solution

Update 2020: The bug is fixed in Powershell 7.1.

Fix `$?` to not be `$false` when native command writes to `stderr`

`$?` is not set to `$false` when native command writes to `stderr`. It is common for native commands to write to `stderr` without intending to indicate a failure. `$?` is set to `$false` only when the native command has a non-zero exit code.

This bug is an unforeseen consequence of PowerShell's prescriptive design for error handling, so most likely it will never be fixed. If your script plays only with other PowerShell scripts, you're safe. However if your script interacts with applications from the big wide world, this bug may bite.

PS> nslookup microsoft.com 2>&1 ; echo $?

False

Problem

Why does PowerShell show the surprising behaviour in the second example below? First, an example of sane behaviour: ``` PS C:\> & cmd /c "echo Hello from standard error 1>&2"; echo "`$LastExitCode=$LastExitCode and `$?=$?" Hello from standard error $LastExitCode=0 and $?=True ``` No surprises. I print a message to standard error (using `cmd`'s `echo`). I inspect the variables `$?` and `$LastExitCode`. They equal to True and 0 respectively, as expected. However, if I ask PowerShell to redirect standard error to standard output over the first command, I get a NativeCommandError: ``` PS C:\> & cmd /c "echo Hello from standard error 1>&2" 2>&1; echo "`$LastExitCode=$LastExitCode and `$?=$?" cmd.exe : Hello from standard error At line:1 char:4 + cmd <<<< /c "echo Hello from standard error 1>&2" 2>&1; echo "`$LastExitCode=$LastExitCode and `$?=$?" + CategoryInfo : NotSpecified: (Hello from standard error :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError $LastExitCode=0 and $?=False ``` My first question, why the NativeCommandError? Secondly, why is `$?` False when `cmd` ran successfully and `$LastExitCode` is 0? PowerShell's documentation about automatic variables doesn't explicitly define `$?`. I always supposed it is True if and only if `$LastExitCode` is 0, but my example contradicts that. Here's how I came across this behaviour in the real-world (simplified). It really is FUBAR. I was calling one PowerShell script from another. The inner script: ``` cmd /c "echo Hello from standard error 1>&2" if (! $?) { echo "Job failed. Sending email.." exit 1 } # Do something else ``` Running this simply as `.\job.ps1`, it works fine, and no email is sent. However, I was calling it from another PowerShell script, logging to a file `.\job.ps1 2>&1 > log.txt`. In this case, an email is sent! What you do outside the script with the error stream affects the internal behaviour of the script. Observing a phenomenon changes the outcome. This feels like quantum physics rather than scripting! [Interestingly: `.\job.ps1 2>&1` may or not blow up depending on where you run it]

Original source

Related problems