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 thoughWrite-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.