PowerShell: Why I can't redirect stdout and stderr of external methods sometimes? PowerShell: Why I can't redirect stdout and stderr of external methods sometimes? powershell powershell

PowerShell: Why I can't redirect stdout and stderr of external methods sometimes?


tl;dr:

  • If you want to capture output from in-process code that produces output via the [Console] API, you must use explicit redirections via [Console]::SetOut() and [Console]::SetError(), the technique you mention in the question.

  • See below for why that is necessary.


PowerShell only allows capturing / redirecting the standard stdout and stderr streams of external (console) programs, inside which, in the case of .NET-based programs, Console.WriteLine() and Console.Out.WriteLine() write to stdout, and Console.Error.WriteLine() writes to stderr.

When running in a console window, PowerShell passes an external program's stdout and stderr streams through to the console (screen) by default; by contrast, the ISE sends stderr output to PowerShell's error stream[1].

> or 1> redirects an external program's stdout (either to a file or to $null to suppress it), 2> redirects stderr[2].
Additionally, assigning output from an external program to a variable captures its stdout output, and sending an external program's output through the pipeline redirects its stdout output to PowerShell's success output stream.


By contrast, you're using the [Console] type's output methods in-process, where no such capturing is possible, because such method calls simply output to the same console that PowerShell itself runs in, without PowerShell knowing about it.[3]

You can cut out the middleman to verify this behavior:

PS> [Console]::WriteLine('hi')  *> $null # Try to suppress ALL output streams.hi  # !! Still prints to the console - PowerShell streams were bypassed.

The only way to (temporarily) redirect [Console] output in-process is to explicitly call .SetOut() and .SetError(), as mentioned in the question.

The reason that 2> $NULL in &{ [System.Net.Dns]::Resolve('bla') } 2> $NULL does work is that the method throws an exception, which PowerShell outputs to its error stream (stream number 2), whose output 2> $NULL effectively suppresses.
Note that, because an exception is thrown, 2> $NULL is only effective because the method call is enclosed in & { ... }; otherwise, the exception would terminate the redirection itself too.
However, with respect to in-process [Console] behavior with no exceptions involved, whether & { ... } is involved or not makes no difference.


Therefore, in order for your custom C# method to integrate with PowerShell's streams - short of using PowerShell APIs directly in your C# code - do the following:

  • use return for what should go to PowerShell's success stream (stream number 1)

  • throw an exception for what should go to PowerShell's error stream (stream number 2), but note that an unhandled exception will by default abort the enclosing statement as a whole.


Alternatively, compile your C# code to an external program, say, godemo.exe:

# With an external executable, redirections work as expected.godemo.exe 1> $null 2> $null 

[1] This divergent behavior in the ISE is problematic; it is discussed in this GitHub issue.

[2] If $ErrorActionPreference = 'Stop' happens to be in effect, any 2> redirection unexpectedly causes a script-terminating error; this problematic behavior is discussed in this GitHub issue.

[3] The methods write to the current console's stdout and stderr streams, which, in the absence of an external redirection, print to the screen.