Closely related to #3996 and #11133
The current behavior of calling external programs _without_ stderr redirection is being discussed in the linked issue.
Either way, simply adding 2>$null
or 2>&1
- indeed, _any_ redirection of stderr (2>...
) - to a command shouldn't fundamentally change its error-handling behavior.
Note that when a host other than the console host is involved (e.g., when in the _ISE_ or when _remoting_), the difference described below does _not_ occur, but the behavior is fundamentally different (and presumably undesired - see #3996).
Note:
Even though the repro steps use external programs, PowerShell commands are equally affected (e.g., & { $ErrorActionPreference = 'stop'; Get-Item NosuchFile 2>$null }
unexpectedly produces an error, whereas -ErrorAction SilentlyContinue
would not).
Similarly, using 2>
with an actual file as the target is also affected (e.g., & { $ErrorActionPreference = 'stop'; whoami badarg 2>stderr.txt }
aborts, and stderr.txt
is empty).
On Unix, replace cmd /c
with sh -c
.
& { $ErrorActionPreference = 'stop'; cmd /c nosuch; $? }
& { $ErrorActionPreference = 'stop'; cmd /c nosuch 2>$null; $? }
Both commands should fundamentally behave the same, which, based on the current default behavior in the console, is to _not_ report an error, with stderr output _passed through_ in the 1st command and stderr output getting _suppressed_ in the other.
'nosuch' is not recognized as an internal or external command,
operable program or batch file.
False
'nosuch' is not recognized as an internal or external command,
operable program or batch file.
False
'nosuch' is not recognized as an internal or external command,
The 2nd half of the error message, operable program or batch file.
, is missing.
Presumably, this happens because:
[System.Management.Automation.ErrorRecord]
instance, cmd
error message happens to span _2_ lines).Note that _any_ 2>
redirection causes the symptom, such as 2>&1
or 2>err.txt
.
Seemingly, the following happens:
Despite _redirection_ of the error stream, the stderr lines are apparently still _passing through_ it, as evidenced by them showing up in $Error
afterwards.
With $ErrorActionPreference = 'stop'
in effect, anything getting written to PowerShell's error stream instantly triggers a function/script-terminating error.
Again, note that without a redirection, stderr lines neither show up in $Error
nor do they cause the script to be terminated based on $ErrorActionPreference = 'stop'
.
PowerShell Core v6.0.0-beta.4 on macOS 10.12.5
PowerShell Core v6.0.0-beta.4 on Ubuntu 16.04.2 LTS
PowerShell Core v6.0.0-beta.4 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.413 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
I am able to repro this. This does look like inconsistent behavior. If the default out-putter is configured then the error message is just displayed. But if error is redirected to a pipe then it goes through MshCommandRuntime processing and ErrorActionPreference is effective, and the stop exception is thrown.
This is done here:
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L634
I disagree w/ expected behavior. I don't think the expected behavior of 2>$null
is documented anywhere. It was an intentional edge-case while I was searching for potential extensions to redirection syntax. You can find other similar cases, e.g.
try {cmd /c whamo! 2>$()} catch { "Dang!" }
P.S. What @mklement0 is referring to as "the 2nd half of the message" is not "missing", or if so then I found it... It's not coming from the native command, it's a PowerShell ErrorRecord that get's it's own slot.
PS> $error.Clear()
PS> cmd /c whamo! 2>$()
PS> $error.Count
2
PS> $error[0]
operable program or batch file.
PS> $error[1]
cmd : 'whamo!' is not recognized as an internal or external command,
At line:1 char:1
+ cmd /c whamo! 2>$()
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: ('whamo!' is not...ternal command,:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
@BurtHarris I've revised the expected-behavior description in the original post in light of the discussion in the linked issue: if the consensus is that stderr output should never generate an error, then the expected behavior is as _now_ described in the initial post.
Yes, _any_ 2>
redirection triggers the symptom, presumably for the reasons @PaulHigin states.
Without $ErrorActionPreference = 'Stop'
in the mix, you do indeed get _all_ stderr lines recorded in $Error
, as an _error record each_, but _with_ $ErrorActionPreference = 'Stop'
, the _1st_ line triggers termination of the pipeline, so the 2nd line never gets recorded in $Error
.
I don't think the expected behavior of 2>$null is documented anywhere.
Indeed, about_Redirection doesn't mention redirecting to $null
, but if it does anything other than _silently discarding the stream_ (as it does with the _success stream_, for instance), we have a problem - as evidenced by this issue.
The unfortunate interaction with $ErrorActionPreference = 'Stop'
is one thing, but the fact that output that you're asking to have _suppressed_ is _still getting recorded_ somewhere - in $Error
- is absolutely counter-intuitive.
Where is this issue? I've been able to repro it across an upgrade of a build version and wonder if I'm missing something since this is such an old thread.
repro: powershell v5.1.17763.316, OS 10.0.17763
no-repro: powershell v5.1.16299.967, OS 10.0.16299
Is the upgrade from 16299 to 17763 going to pull this in, and any projection of a fix? We've seen unreliability in our CI builds bailing out at odd times when they hit a machine with this newer powershell installed.
Hi.
Let me chime in.
I have a script which cheks if a python module is installed
python -c "import blah" 2>$null
if ($?) {
# Run expensive installations
}
then I enabled strict error handling $ErrorActionPreference = "stop"
and whenever that line runs (and the module is not setup), the whole script aborts instead of running the following steps.
I tried also with python -c ... 2>&1 | out-null
and the NativeCommandError
is always there.
This seems like a clear bug to me. If the error stream is being redirected to $null
, that means that the user is explicitly ignoring any errors.
So far, the only work around is to use try{} catch{}
.
Testcase
Write-Host You should not see an error
$ErrorActionPreference = "stop"
python -c "import notfound_foobar" 2>$null
Write-Host PASS
Have just hit this myself. Calling an exe and redirecting the errorstream to a file (it doesn't actually contain an error, just additional info), and had been happily using it for a year or two. I turned ErrorActionPreference to "Stop" globally, and suddenly it fails, despite there not being any actual errors.
Related to #3996, want to address similarly
We discussed this in the @PowerShell/powershell-committee and agreed this is a dupe of #3996
Most helpful comment
Have just hit this myself. Calling an exe and redirecting the errorstream to a file (it doesn't actually contain an error, just additional info), and had been happily using it for a year or two. I turned ErrorActionPreference to "Stop" globally, and suddenly it fails, despite there not being any actual errors.