Discarding Output

If you'd like to discard unwanted output from commands, understanding and managing output streams is key. And if all else fails, you can always temporarily disable Out-Default.

When you write PowerShell scripts, you typically want to control the information sent back to the user. So when your script uses commands, you don’t necessarily want these commands to emit their output directly to the user.

Understanding Streams

In a perfect world, controlling and discarding command output is simple because commands typically use PowerShell streams to emit information, and you can redirect streams and assign them to variables or $null.

Suppressing Output Stream

For example, if all you want is creating a new folder, you can direct the output stream to $null and discard the information sent back by New-Item:

# create a new folder and discard returned information
$null = New-Item -Path 'c:\newfolder' -ItemType Directory

Showing Or Hiding Streams

In fact, there is a series of preference variables that allow you to selectively show or hide various streams:

Get-Variable -Name *preference -Exclude *confirm*,*whatif*,*progress*   

Simply set a stream to Continue to make it visible, and to SilentlyContinue to hide it:

Name                  Value
----                  -----
DebugPreference       SilentlyContinue
ErrorActionPreference Continue
InformationPreference SilentlyContinue
VerbosePreference     SilentlyContinue
WarningPreference     Continue

Each stream corresponds with Write-cmdlets and a stream number:

Stream Cmdlet Id
Debug Write-Debug 5
Error Write-Error 2
Information Write-Information 6
Verbose Write-Verbose 4
Warning Write-Warning 3
Output Write-Output 1
Host Write-Host 6

There are two streams that are not controlled by preference variables and are always visible: Output and Host.

The Output stream is the one that is piped to downstream cmdlets and assigned to variables. The Host stream shares the stream id 6 with the Information stream and can be silenced by redirecting stream 6.

Redirecting Streams

By default, only the Output stream is assigned to variables. All other streams either are emitted directly to the console, or are hidden (controlled by your preference variables).

To include the information from other streams in your variables, use redirection: take the stream id to redirect the stream to the output stream (&1).

This line redirects errors and warnings to the output stream and assigns them to a variable:

# include errors and warnings in variable results:
$all = Get-Process -FileVersionInfo 2>&1 3>&1

After you run it, $all contains the results plus all errors and warnings.

Special Case: Write-Host

Write-Host did not play by the rules and bypassed the stream system up until PowerShell 5 (thanks Justin for pointing out). Ever since, Write-Host is sharing the Information stream, and that’s important to know. Because of this, it is ok to use Write-Host again:

  • Write-Host: use this cmdlet for messages that a user should always see. Even though Write-Host is using the Information stream, it is not affected by any preference variable such as $InformationPreference, and always set to Continue.
  • Write-Information: use this cmdlet for messages that a user should not see by default ($InformationPreference defaults to SilentlyContinue) but that a user might choose to turn on.

Here is a proof of concept illustrating how you can hide messages emitted by Write-Host:

& {
Write-Warning "A Warning"
"Regular Output"
Write-Host "This will dissappear!"
} 6>$null

By redirecting stream 6 to null, all outputs from Write-Host and Write-Information are discarded. You can append 6>$null to any command or script call.

While at it, there are two ways of discarding streams:

# two ways of discarding streams:
Write-Warning "A Warning" 3>$null
$null = Write-Warning "A Warning" 3>&1

The first line redirects to $null which really is the same as redirecting to an empty string because the redirection operator takes only strings, so $null is converted to a string:

Write-Warning "A Warning" 3>''

This is a special use case of the redirection operator. Typically, it expects a filename and writes the stream to this file. When you specify an empty string, the stream is nuked.

In contrast, the second line redirects the stream to the output stream (id #1) so the stream is merged into the output and can be assigned to variables, including the special variable $null.

This is a fundamentally different approach. The first line enables you to selectively discard streams that you want to hide. The second line enables you to assign or discard the entire mixture of output stream and any other stream you merged into the output stream.

Why Some Commands Are Ugly

Occasionally, you may stumble across commands that don’t play by the rules and write output that simply can’t be suppressed.

For example, Get-WindowsUpdateLog is a command on Windows 10 that extracts update information from .etl files and generates a log file for you:

Get-WindowsUpdateLog

When you run this command, it emits a ton of information, and there doesn’t seem to be a way to hide or discard this information:

# redirecting streams won't suppress the output
$null = Get-WindowsUpdateLog *>&1

Even if you redirect all streams (including stream 6) to the output stream and send everything to $null, the messages still show in the console.

Writing Directly To Host

This problem occurs whenever commands write directly to the console and bypass the stream mechanism. Here is a line of code that illustrates the problem:

# cannot be silenced:
& { [Console]::WriteLine("Hello") } *>$null

Silencing All Output

To silence direct console output and discard all output, you can temporarily disable the internal PowerShell command Out-Default. This command is used privately to write directly to the console.

Read more about Out-Default if you like.

While you really can’t disable the command (it is hard-coded to PowerShell), you can replace it with your own functions, effectively overwriting it. So here is some example code that runs Get-WindowsUpdateLog without showing all the status messages:

# temporarily overwrite Out-Default
function Out-Default {}

# run your code (guaranteed no output)
Get-WindowsUpdateLog

# test any other direct console write
[Console]::WriteLine("Hello")

# restore Out-Default
Remove-Item -Path function:Out-Default

As long as Out-Default is replaced by your own empty function, PowerShell emits no output whatsoever. This applies to scripts as well as interactive commands you may invoke. So make sure you remove your function Out-Default once you are ready to see output again.

This Out-Default trick isn’t as edge-casy as you may think. When you browse user groups you’ll discover plenty of threads that deal with this issue.

PowerShell users have resorted to all kinds of creative and complex workarounds to discard direct console output, i.e. running code as a job or in another PowerShell instance with a hidden window.

You know now: that’s a bit of overkill. Simply shadow Out-Default for as long as you want to silence PowerShell.