diff --git a/src/System.Management.Automation/engine/NativeCommandProcessor.cs b/src/System.Management.Automation/engine/NativeCommandProcessor.cs index 85b675a9ae9..0912afad435 100644 --- a/src/System.Management.Automation/engine/NativeCommandProcessor.cs +++ b/src/System.Management.Automation/engine/NativeCommandProcessor.cs @@ -815,12 +815,6 @@ private ProcessOutputObject DequeueProcessOutput(bool blocking) { if (blocking) { - if (_stdOutByteTransfer is not null) - { - _stdOutByteTransfer.EOF.GetAwaiter().GetResult(); - return null; - } - // If adding was completed and collection is empty (IsCompleted == true) // there is no need to do a blocking Take(), we should just return. if (!_nativeProcessOutputQueue.IsCompleted) @@ -842,17 +836,9 @@ private ProcessOutputObject DequeueProcessOutput(bool blocking) return null; } - else - { - if (_stdOutByteTransfer is not null) - { - return null; - } - ProcessOutputObject record = null; - _nativeProcessOutputQueue.TryTake(out record); - return record; - } + _nativeProcessOutputQueue.TryTake(out ProcessOutputObject record); + return record; } /// @@ -860,21 +846,38 @@ private ProcessOutputObject DequeueProcessOutput(bool blocking) /// private void ConsumeAvailableNativeProcessOutput(bool blocking) { - if (!_isRunningInBackground) + if (_isRunningInBackground) { - if (_nativeProcess.StartInfo.RedirectStandardOutput || _nativeProcess.StartInfo.RedirectStandardError) + return; + } + + bool stdOutRedirected = _nativeProcess.StartInfo.RedirectStandardOutput; + bool stdErrRedirected = _nativeProcess.StartInfo.RedirectStandardError; + if (stdOutRedirected && _stdOutByteTransfer is not null) + { + if (blocking) { - ProcessOutputObject record; - while ((record = DequeueProcessOutput(blocking)) != null) - { - if (this.Command.Context.CurrentPipelineStopping) - { - this.StopProcessing(); - return; - } + _stdOutByteTransfer.EOF.GetAwaiter().GetResult(); + } + + if (!stdErrRedirected) + { + return; + } + } - ProcessOutputRecord(record); + if (stdOutRedirected || stdErrRedirected) + { + ProcessOutputObject record; + while ((record = DequeueProcessOutput(blocking)) != null) + { + if (this.Command.Context.CurrentPipelineStopping) + { + this.StopProcessing(); + return; } + + ProcessOutputRecord(record); } } } @@ -887,7 +890,7 @@ internal override void Complete() if (!_isRunningInBackground) { // Wait for input writer to finish. - if (!UpstreamIsNativeCommand) + if (!UpstreamIsNativeCommand || _nativeProcess.StartInfo.RedirectStandardError) { _inputWriter.Done(); } @@ -1772,7 +1775,7 @@ public ProcessOutputHandler( // we incrementing refCount on the same thread and before running any processing // so it's safe to do it without Interlocked. - if (process.StartInfo.RedirectStandardOutput) + if (process.StartInfo.RedirectStandardOutput && stdOutDestination is null) { _refCount++; } diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 index d166ca265c7..542f1724303 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 @@ -213,6 +213,12 @@ Describe "Native Command Processor" -tags "Feature" { Wait-UntilTrue -sb { (Get-Process mmc).Count -gt 0 } -TimeoutInMilliseconds 5000 -IntervalInMilliseconds 1000 | Should -BeTrue Get-Process mmc | Stop-Process } + + It 'Can redirect stdout and stderr to different files' { + testexe -stderrandout testing > $TestDrive/stdout.txt 2> $TestDrive/stderr.txt + Get-Content $TestDrive/stdout.txt | Should -Be testing + Get-Content $TestDrive/stderr.txt | Should -Be gnitset + } } Describe "Open a text file with NativeCommandProcessor" -tags @("Feature", "RequireAdminOnWindows") { diff --git a/test/tools/TestExe/TestExe.cs b/test/tools/TestExe/TestExe.cs index a91e2141b58..39c1d2b2ecb 100644 --- a/test/tools/TestExe/TestExe.cs +++ b/test/tools/TestExe/TestExe.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.IO; using System.Globalization; +using System.Linq; namespace TestExe { @@ -36,6 +37,10 @@ private static int Main(string[] args) case "-stderr": Console.Error.WriteLine(args[1]); break; + case "-stderrandout": + Console.WriteLine(args[1]); + Console.Error.WriteLine(new string(args[1].ToCharArray().Reverse().ToArray())); + break; case "-readbytes": ReadBytes(); break;