'Is there a way to set a variable up to place output to stdout or null?
I would like to set up a variable in my code that would ultimately define if I'll see some output or not.
"hello"
writes to stdout"hello" > $null
supresses output
My idea is something like this:
$debugOutputSwitch = $true
$outputVar = $null
if ($debugOutputSwitch){ $outputVar = **STDOUT** }
...
Write-Host "Something I want out anyway"
"Something I might not want on STDOUT" > $outputVar
If this general idea is a way to go, then STDOUT
is what I'm looking for
If this idea is completely wrong...well...then I'm lost
Solution 1:[1]
What you want to read up on are output streams and redirection in Powershell. This includes information on all of the different output streams and how to control their relevance using built-in constructs. Just like there are the Write-Host
and Write-Output
cmdlets, there are also several others that control which stream to write to.
About the Output Streams
There are 6 streams in total. Make note of their numbers, because these stream identifiers are used to control which streams to redirect:
- 1 - Success Stream - This stream is used when passing information along the Powershell Pipeline. This is the "default" stream, but can also be written to with
Write-Output
. - 2 - Error Stream - Errors should be written to this stream. Can be written to with
Write-Error
, accompanied by further error information. - 3 - Warning Stream - Used to write warning information. Can be written to with
Write-Warning
. - 4 - Verbose Stream - Used to write verbose output. Does not display by default but can be made to display by either setting
$VerbosePreference = "Continue"
, or by using the[CmdletBinding()]
attribute on a function or script and passing in the-Verbose
flag. Write to the verbose stream withWrite-Verbose
. - 5 - Debug Stream - Used to write to the debug stream, and optionally trigger a breakpoint. Does not display or trigger a breakpoint by default, but can be controlled with the
$DebugPreference
variable, or by using the[CmdletBinding()]
attribute on a script or function and using the-Debug
flag. You can write to the debug stream by using theWrite-Debug
cmdlet. - 6 - Information Stream - Can be written to by
Write-Host
. This is the console host output and is not part of the pipeline.
Redirecting Streams
You can use redirection operators to redirect other streams to the success stream as well. Each stream above has a number associated with it. This is the numeric representation of each stream.
The redirection operators are as follows:
>
- Redirect success stream to file (overwrite)#>
- Redirect the#
stream to file (e.g.2> somefile.txt
)>>
- Redirect success stream to file (appends, you can also use a numbered stream as with the overwrite file operator)>&1
- Redirect any stream to success stream (note that unlike the other redirection operators you can only redirect to the success stream. Using other stream identifiers will result in an error).
Also note that in place of a stream number, you can use *
which will redirect all streams at the same time.
Here are some examples of redirecting output from one stream to another (if you're familiar with it, it's somewhat UNIX-y):
# Write success stream to file
Write-Output "Here is some text for a file" > .\somefile.txt
# Write error stream to file (you have to first
Write-Error "Some error occurred" 2> .\somefile.txt
# Redirect all error output to the success stream
$myErrorOutput = Write-Error "My error output" 2>&1
# Append all script output streams to a single file
Get-OutputFromAllStreams.ps1 *>> somefile.txt
Output to a File and the Pipeline Simultaneously
You can redirect the output stream to a file and the pipeline at the same time as well, using the Tee-Object
cmdlet. This also works with variables, too:
$myString = "My Output" | Tee-Object -FilePath .\somefile.txt
$myString2 = "My Output 2" | Tee-Object -Variable varName
Sample function to show how to use the different Write-
cmdlets
Note how the following function is decorated with the [CmdletBinding()]
attribute. This is key in making the -Verbose
and -Debug
switches work without you having to define them yourself.
function Write-DifferentOutputs {
[CmdletBinding()]
# These all visible by default but only the output stream is passed down the pipeline
Write-Output "Output stream"
Write-Warning "Warning stream"
Write-Error "Error stream"
Write-Host "Information stream"
# These are not visible by default, but are written when the `-Verbose` or `-Debug` flags are passed
# You can also manually set the $VerbosePreference or $DebugPreference variables to control this without parameters
Write-Verbose "Verbose stream"
Write-Debug "Debug stream"
}
Call the above function with the -Verbose
or -Debug
switches to see how the behavior differs, and also call it with neither flag.
Redirecting output to $null
if you really need to
If there is output that you never want to see or for some other reason using the Write-
cmdlets to write to the Verbose
or Debug
streams isn't an option, you can still redirect output to $null
or make use of the Out-Null
cmdlet. Recall the numbered streams at the top of this answer, they will be referenced here:
Using redirection
# Don't forget that *> redirects ALL streams, and may be what you want
Write-Output 'Success Stream' > $null
Write-Error 'Error Stream' 2> $null
Write-Warning 'Warning Stream' 3> $null
Write-Verbose 'Verbose Stream' 4> $null
Write-Debug 'Debug Stream' 5> $null
Write-Host 'Information Stream (yes you can suppress/redirect me)' 6> $null
Using Out-Null
Remember, you can redirect other streams to the success stream by redirecting the output to &1
.
# Remember, to pass information on the pipeline
# it MUST be on the success stream first
# Don't forget that *> redirects ALL streams, and may be what you want
Write-Output 'Success Stream' | Out-Null
Write-Error 'Error Stream' 2>&1 | Out-Null
Write-Warning 'Warning Stream' 3>&1 | Out-Null
Write-Verbose 'Verbose Stream' 4>&1 | Out-Null
Write-Debug 'Debug Stream' 5>&1 | Out-Null
Write-Host 'Information Stream (yes you can suppress/redirect me)' 6>&1 | Out-Null
When using Out-Host
is appropriate ("Don't Cross the Streams")
Warning: Unlike
Write-Host
,Out-Host
does not output to the information stream. Instead, it outputs directly to the host console. This makes redirection of anything written directly toOut-Host
impossible short of usingStart-Transcript
or using a custom PowerShell host. Note that information written to the console host is still visible to external applications which may be watching the output of PowerShell, as ultimately evenOut-Host
output makes it to STDOUT.
Calling yourself Out-Host
is usually redundant. By default, PowerShell sends all unassigned output on the success stream here via way of the Out-Default
cmdlet (which you should never callOut-Default
directly). That said, one useful invocation of Out-Host
is to synchronously output formatted object data to the console:
Note: You can redirect information from other output streams and output to
Out-Host
as well, but there is no reason to do so. Object data will only remain intact on the success stream, the other streams will first convert an object to itsToString()
representation prior to redirection. This is also why piping the object toOut-Host
in this case is preferable toWrite-Host
.
Get-Process msedge | Out-Host
One of the caveats of the different output streams is that there is no synchronicity between streams. Normally this is not an issue as PowerShell executes instructions line by line in order, and with the exception of Write-Output
success stream this is not a problem with the other streams. However, many types will have a computed for-display attribute which is computed asynchronously from the script execution before the information is sent to Out-Default
.
This can result in the displayed object data being intermingled with other output streams which are written to the console host. In some cases, this can even result in loss of information written to the console. "Crossing the streams", if you will, as it pertains to how the rendered output may look.
Consider the following example and output. This does not showcase the streams intermingling, but consider the trouble you would have parsing the output externally if Write-Host "end `n"
were written in the middle of the table:
Write-Host "start `n"
Get-LocalUser
Write-Host "end `n"
And the output:
start
end
Name Enabled Description
---- ------- -----------
Administrator True
DefaultAccount False A user account managed by the system.
Disabled False Built-in account for guest access to the computer/domain
In particular this is problematic for types which define a table format that must calculate the column width before sending the formatted data to Out-Host
for display. Types which pre-define the table width or do not format output as a table at all do not have this problem. Out-Default
can take up to 300ms to calculate column width.
When Out-Host
is called explicitly as part of a pipeline, however, the table width calculation is skipped for these objects as the object data never makes it to Out-Default
. This is useful primarily to ensure that object data intended to be written to the console is done so in the correct order. The downside is that table columns may not be wide enough to accommodate all of your data on each row.
This all said, if you must process the console output of a script, it is recommended to format the data you wish to process into a string and use Write-Host
instead, or use another method to get the data somewhere suitable for external processing. for-display
formatting is not intended for use with external processing.
@mklement1's answer here dives further into the details about this if you are curious to learn more about this problem.
Redirecting whole command outputs to Write-
cmdlets
You can easily pipe all output of a command or cmdlet to one of the Write-
cmdlets. I'll use the Write-DifferentOutputs
provided earlier in my example below, but this will work with any cmdlet, script, or command you run:
Write-DifferentOutputs *>&1 | Write-Verbose
What the above will do is only show the command output if $VerbosePreference = $Continue
, or if you passed -Verbose
as an argument to your script or function.
In Summarium
In your original question, you are attempting to reinvent a wheel that Powershell already supports fairly well. I would suggest that you learn how to make use of the different Write-Output
cmdlets for each stream and especially learn how to make use of the Write-Warning
, Write-Verbose
, Write-Error
, and Write-Debug
cmdlets.
Solution 2:[2]
All right. Thanks to all the brainiacs here for the motivation. This answer may not be the best way to go about it, but it works!
Two things you need to understand to achieve this:
- If you are used to using
Write-Host
, it won't work, you'll have to go withWrite-Output
. - You may have to learn to use a block of script as a function parameter.
One is self explanatory, so here's how to attain #2:
Function Test-SctiptBlockParam {
Param(
$scriptblock
)
if ($debugOutput) {
Invoke-Command $scriptblock
} else {
(Invoke-Command $scriptblock) > $null
}
}
Test-SctiptBlockParam -scriptblock { Write-Output "I want to see on the STDOUT sometimes" }
Solution 3:[3]
Again may not be the best way, but works.
As @Bender the Greatest has already mentioned a function with [CmdletBinding] should help the purpose.
I'll try to provide a very basic example -
A selective logger function like this -
function Selective-Log {
[CmdletBinding()]
param(
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$Message,
[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidateSet('Info','Warning','Error')]
[string]$Severity = 'Info'
)
if($Severity -eq 'Info'){
Write-Output $Message > $null
}else{
Write-Output $Message
}}
Now when you use it in your scripts as
Selective-Log -Message Test1 -Severity Info
it wouldn't log anything
Then if you wish to log you'd choose severity other than Info
Selective-Log -Message Test2 -Severity Error
you could also use it like Selective-Log Test2 Error
Hope this helps someone
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Bender the Greatest |
Solution 3 | Satish Kumar Nadarajan |