diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index f6993a03a0a..e44ea07962c 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -123,6 +123,9 @@ static ExperimentalFeature() new ExperimentalFeature( name: "PSNativePSPathResolution", description: "Convert PSPath to filesystem path, if possible, for native commands"), + new ExperimentalFeature( + name: "PSNotApplyErrorActionToStderr", + description: "Don't have $ErrorActionPreference affect stderr output"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index af4716f3309..eea82e1ab65 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -2831,61 +2831,62 @@ internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreferenc this.PipelineProcessor.LogExecutionError(_thisCommand.MyInvocation, errorRecord); } - ActionPreference preference = ErrorAction; - if (actionPreference.HasValue) + if (!(ExperimentalFeature.IsEnabled("PSNotApplyErrorActionToStderr") && isNativeError)) { - preference = actionPreference.Value; - } + ActionPreference preference = ErrorAction; + if (actionPreference.HasValue) + { + preference = actionPreference.Value; + } - // No trace of the error in the 'Ignore' case - if (ActionPreference.Ignore == preference) - { - return; // do not write or record to output pipe - } + // No trace of the error in the 'Ignore' case + if (ActionPreference.Ignore == preference) + { + return; // do not write or record to output pipe + } - // 2004/05/26-JonN - // The object is not written in the SilentlyContinue case - if (ActionPreference.SilentlyContinue == preference) - { - AppendErrorToVariables(errorRecord); - return; // do not write to output pipe - } + // 2004/05/26-JonN + // The object is not written in the SilentlyContinue case + if (ActionPreference.SilentlyContinue == preference) + { + AppendErrorToVariables(errorRecord); + return; // do not write to output pipe + } - if (ContinueStatus.YesToAll == lastErrorContinueStatus) - { - preference = ActionPreference.Continue; - } + if (ContinueStatus.YesToAll == lastErrorContinueStatus) + { + preference = ActionPreference.Continue; + } - switch (preference) - { - case ActionPreference.Stop: - ActionPreferenceStopException e = - new ActionPreferenceStopException( - MyInvocation, - errorRecord, - StringUtil.Format(CommandBaseStrings.ErrorPreferenceStop, - "ErrorActionPreference", - errorRecord.ToString())); - throw ManageException(e); + switch (preference) + { + case ActionPreference.Stop: + ActionPreferenceStopException e = + new ActionPreferenceStopException( + MyInvocation, + errorRecord, + StringUtil.Format(CommandBaseStrings.ErrorPreferenceStop, + "ErrorActionPreference", + errorRecord.ToString())); + throw ManageException(e); + + case ActionPreference.Inquire: + // ignore return value + // this will throw if the user chooses not to continue + lastErrorContinueStatus = InquireHelper( + RuntimeException.RetrieveMessage(errorRecord), + null, + true, // allowYesToAll + false, // allowNoToAll + true, // replaceNoWithHalt + false // hasSecurityImpact + ); + break; + } - case ActionPreference.Inquire: - // ignore return value - // this will throw if the user chooses not to continue - lastErrorContinueStatus = InquireHelper( - RuntimeException.RetrieveMessage(errorRecord), - null, - true, // allowYesToAll - false, // allowNoToAll - true, // replaceNoWithHalt - false // hasSecurityImpact - ); - break; + AppendErrorToVariables(errorRecord); } - // 2005/01/20 Do not write the object to $error if - // ManageException has already done so - AppendErrorToVariables(errorRecord); - // Add this note property and set its value to true for F&O // to decide whether to call WriteErrorLine or WriteLine. // We want errors to print in red in both cases. diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index d8eec255184..390b461fb90 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -100,19 +100,6 @@ Describe "ConsoleHost unit tests" -tags "Feature" { { & $powershell -outp blah -comm { $input } } | Should -Throw -ErrorId "IncorrectValueForFormatParameter" } - It "Verify Validate Dollar Error Populated should throw exception" { - $origEA = $ErrorActionPreference - $ErrorActionPreference = "Stop" - $a = 1,2,3 - $e = { - $a | & $powershell -noprofile -command { wgwg-wrwrhqwrhrh35h3h3} - } | Should -Throw -ErrorId "CommandNotFoundException" -PassThru - - $e.ToString() | Should -Match "wgwg-wrwrhqwrhrh35h3h3" - - $ErrorActionPreference = $origEA - } - It "Verify Validate Output Format As Text Explicitly Child Single Shell does not throw" { { "blahblah" | & $powershell -noprofile -out text -com { $input } diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 index 12b1349f716..e582f55a044 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 @@ -140,6 +140,10 @@ Describe "Native Command Processor" -tags "Feature" { [Console]::OutputEncoding = $originalOutputEncoding } } + + It '$ErrorActionPreference does not apply to redirected stderr output' -Skip:(!$EnabledExperimentalFeatures.Contains('PSNotApplyErrorActionToStderr')) { + pwsh -noprofile -command '$ErrorActionPreference = ''Stop''; testexe -stderr stop 2>$null; ''hello''; $error' | Should -BeExactly 'hello' + } } Describe "Open a text file with NativeCommandProcessor" -tags @("Feature", "RequireAdminOnWindows") { diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 index 2970f7ae7a9..59702938375 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 @@ -21,8 +21,7 @@ Describe "Native streams behavior with PowerShell" -Tags 'CI' { # this check should be the first one, because $error is a global shared variable It 'should not add records to $error variable' { - # we are keeping existing Windows PS v5.1 behavior for $error variable - $error.Count | Should -Be 9 + $error.Count | Should -Be 0 } It 'uses ErrorRecord object to return stderr output' { diff --git a/test/tools/TestExe/TestExe.cs b/test/tools/TestExe/TestExe.cs index 09606f43e9d..c3c936521b0 100644 --- a/test/tools/TestExe/TestExe.cs +++ b/test/tools/TestExe/TestExe.cs @@ -24,6 +24,9 @@ static int Main(string[] args) // Used to test functionality depending on $LASTEXITCODE, like &&/|| operators Console.WriteLine(args[1]); return int.Parse(args[1]); + case "-stderr": + Console.Error.WriteLine(args[1]); + break; default: Console.WriteLine("Unknown test {0}", args[0]); break;