Is there a way to set a variable up to place output to stdout or null? - powershell

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

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 withWrite-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 with Write-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
You can also redirect target streams per command: The following example (using the earlier Write-DifferentOutputs function) redirects all streams except for the Error and Success streams:
Note: You are not limited to redirecting targeted streams only to $null.
Write-DifferentOutputs 6>$null 5>$null 4>$null 3>$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 to Out-Host impossible short of using Start-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 its ToString() representation prior to redirection. This is also why piping the object to Out-Host in this case is preferable to Write-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.

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 with Write-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" }
Finally, here is an example of my output and code

Related

PowerShell Forms. How come in my Form Closing event If I can't call a Function or use Write-output, but it works if I use Write-Host

I have a small form I'm working on but I have something I'm confused about. I have a closing event
$Form.Add_Closing({}) In there I'm wanting to stop a custom logging module but it doesn't reflect the output to the console, same if I use write-output. If I use Write-Host though, that reflects to the console. Does the Closing event just have any real output capability?
$Form.Add_Closing({
# my logging function - doesn't work
Write-Log -Stop
# Write-Output - doesn't work
Write-Output 'Test message'
# Write-Host - does work
Write-Host 'Another Test message'
})
The problem applies to all events, not just Closing:
Inside a PowerShell script block serving as a .NET event delegate:
You can call arbitrary commands...
... but their success-stream (pipeline) output is discarded.
However, output to any of PowerShell's other output streams does surface in the caller's console, as you've experienced with Write-Host.
Therefore, if you simply want to print the called commands' success output to the caller's display, you can pipe them to Out-Host:
$Form.Add_Closing({
Write-Log -Stop | Out-Host
})
Note:
Out-Host's output - unlike Write-Host's - can fundamentally neither be captured nor suppressed.
Output from Write-Host, which since v5 writes via the information stream, can be suppressed with 6>$null, and in principle be captured via the common -InformationVariable parameter, if your script is an advanced script and it is invoked with, say, ./yourScript -InformationVariable capturedInfoStream.
However, this does not work with Write-Host calls made inside event-delegate script blocks.
If you want to collect success output emitted from event-delegate script blocks for later use in the script (which also allows you to control if the collected output is sent to the script's caller or not), create a list in the script scope, to which you can append from the event-delegate script blocks:
# Initialize a list to collect event-delegate output in.
$outputFromEventDelegates = [Collections.Generic.List[object]] #()
# ...
$Form.Add_Closing({
# Call the function of interest and add its output to the
# script-scope list.
$outputFromEventDelegates.AddRange(
#(Write-Log -Stop)
)
})
# ... after the .ShowDialog() call
# Now you can access all collected output.
Write-Verbose -Verbose "Output collected from event delegates:"
$outputFromEventDelegates

Another PowerShell function return value and Write-Output

this has been beaten to death but can't find an exact solution for my problem.
I have a PowerShell script that can be run from the command line or from a scheduled task. I'm using the following line
Write-Output "Updating user $account" | Tee-Object $logfile -Append
to write relevant information to the screen and a log file. I need both because when run from a command line, I can physically see what's going on but when run from a scheduled task, I have no visibility to its output hence the log file.
Thing is, I'm modifying my code to use functions but as you might already know, Write-Output messes up the return values of functions when used within said functions.
What could I do that would do something similar to what I stated above without affecting the function's return value?
Thanks.
Just write to a log file. When running from the console, open another console and tail the log file.
Get-Content 'C:\path\to\the\logfile.txt' -Tail 10 -Wait
Assuming PowerShell version 5 or higher, where Write-Host writes to the information output stream (stream number 6), which doesn't interfere with the success output stream (stream number 1) and therefore doesn't pollute your function's data output:
The following is not a single command, but you could easily wrap this in a function:
Write-Host "Updating user $account" -iv msg; $msg >> $logfile
The above uses the common -InformationVariable (-iv) parameter to capture Write-Host's output in variable $msg (note how its name must be passed to -iv, i.e. without the leading $).
The message captured in $msg is then appended to file $logfile with >>, the appending redirection operator.
Note: >> is in effect an alias for Out-File -Append, and uses a fixed character encoding, both on creation and appending.
Use Add-Content and its -Encoding parameter instead, if you want to control the encoding.
Instead of explicitly writing each log line to a file, you may want to use a different approach that references the log file only at one location in the code.
Advantages:
Easy to change log path and customize the log output (e. g. prepending a timestamp), without having to modify all code locations that log something.
Captures any kind of messages, e. g. also error, verbose and debug messages (if enabled).
Captures messages of 3rd party code aswell, without having to tell them the name of the log file.
Function SomeFunction {
Write-Host "Hello from SomeFunction" # a log message
"SomeFunctionOutput" # Implicit output (return value) of the function.
# This is short for Write-Output "SomeFunctionOutput".
}
Function Main {
Write-Host "Hello from Main" # a log message
# Call SomeFunction and store its result (aka output) in $x
$x = SomeFunction
# To demonstrate that "normal" function output is not affected by log messages
$x -eq "SomeFunctionOutput"
}
# Call Main and redirect all of its output streams, including those of any
# called functions.
Main *>&1 | Tee-Object -FilePath $PSScriptRoot\Log.txt -Append
Output:
Hello from Main
Hello from SomeFunction
True
In this sample all code is wrapped in function Main. This allows us to easily redirect all output streams using the *>&1 syntax, which employs the redirection operator to "merge" the streams. This means that all commands further down the pipeline (in this example Tee-Object) receive any script messages that would normally end up in the console (except when written directly to the console, which circumvents PowerShells streams).
Possible further improvements
You may want to use try/catch in function Main, so you also capture script-terminating errors:
try {
SomeFunction # May also cause a script-terminating error, which will be catched.
# Example code that causes a script-terminating error
Write-Error "Fatal error" -ErrorAction Stop
}
catch {
# Make sure script-terminating errors are logged
Write-Error -ErrorRecord $_ -ErrorAction Continue
}

Can I Write-Warning in a powershell script without a newline at the end?

I want to print a Warning in PowerShell as a prompt and then read the answer on the same line. The problem is that Write-Warning prints a newline at the endof the message, and one alternative, Read-Host -Prompt, doesn't print the prompt to the warning stream (or print in yellow). I've seen Write-Warning -WarningAction Inquire, but I think that's a little verbose and offers options I don't want.
The best I've done is:
$warningMsg= "Something is wrong. Do you want to continue anyway Y/N? [Y]:"
Write-Host -ForegroundColor yellow -NoNewline $warningMsg
$cont = Read-Host
This works great--prints the yellow prompt and then reads the input on the same line--but I'm wondering about the warnings I've seen against using Write-Host, if it's more appropriate to figure out some way to print to the warning stream without a newline. Is there a way to do that? I've noticed that Write-Host seems to be a wrapper to write to the Info stream, but I don't see any way to write to warnings without a new line ([Console]::Warning.WriteLine() doesn't exist for example).
You can't do what you want with Write-Warning.
Therefore, I will answer regarding your other concern.
Write-Host is perfectly fine to use in a PowerShell 5+ script.
If you look at the articles recommending against its use, you will notice that the vast majority (if not all) were written before the introduction of PowerShell 5.
Nowadays, Write-Host is a wrapper around Write-Information.
The official documentation confirms this:
Starting in Windows PowerShell 5.0, Write-Host is a wrapper for
Write-Information This allows you to use Write-Host to emit output to
the information stream. This enables the capture or suppression of
data written using Write-Host while preserving backwards
compatibility.
The $InformationPreference preference variable and -InformationAction
common parameter do not affect Write-Host messages. The exception to
this rule is
-InformationAction Ignore, which effectively suppresses
Write-Host output.
Writing to the information stream using Write-Host and / or Write-information won't create problems with your output string.
Stream # Description Introduced in
1 Success Stream PowerShell 2.0
2 Error Stream PowerShell 2.0
3 Warning Stream PowerShell 3.0
4 Verbose Stream PowerShell 3.0
5 Debug Stream PowerShell 3.0
6 Information Stream PowerShell 5.0
* All Streams PowerShell 3.0
Bonus
You can also control the visibility of the information stream if you use an advanced function through the -InformationAction parameter, provided you also bind the given parameter value to the Write-Host statements in the function.
For instance, if you wanted to disable the Information stream by default unless requested otherwise:
function Get-Stuff {
[CmdletBinding()]
param ()
if (!$PSBoundParameters.ContainsKey('InformationAction')) {
$InformationPreference = 'Ignore'
}
Write-Host 'This is the stuff' -InformationAction $InformationPreference -ForegroundColor Green
}
# Hidden by default
Get-Stuff
# Force it to show
Get-Stuff -InformationAction Continue
Note
While technically it is not possible to use Write-Warning -NoNewLine, you could look into manipulating the cursor position and resetting it to the end of the previous line, therefore doing the same.
However, I have limited experience with that and my observations regarding the subject were that you might end up having to create exceptions to comply with limitations of some console environments. In my opinion, this is a bit overkill...
Additional references
About_redirections

How to combine PowerShell runspace stderr, stdout etc into a single stream

I'm looking for the equivalent of the PowerShell pipeline redirection *>&1 when running a job.
I run the jobs roughly like this:
$Instance = [PowerShell]::Create()
$Instance.AddScript($CommandList)
$Result = $Instance.BeginInvoke()
$Instance.EndInvoke($Result)
The trouble is output is divided into multiple streams and to report it I must do this:
$Instance.Streams.Debug
$Instance.Streams.Error
$Instance.Streams.Information
This groups messages by type rather than interleaving them so that there is no good way to tell where, within an execution, a given error was thrown. If they were combined, the errors would appear immediately after relevant Write-Host statements.
There appear to be 5 streams(debug, error, information, progress, verbose and warning) and I'd like to combine them all although simply combining error and information would be a huge step forward.
I looked around the $Instance object and tried to find something under InitialSessionState for passing to Create() with nothing obvious presenting itself.
To access output from all streams in output order when using the PowerShell SDK, you'll have to resort to *>&1 as well:
$Instance = [PowerShell]::Create()
# Example commands that write to streams 1-3.
$CommandList = 'Write-Output 1; Write-Error 2; Write-Warning 3'
# Wrap the commands in a script block (`{...}`) and call it using
# `&`, the call operator, which allows you to apply redirection `*>&1`
$null = $Instance.AddScript('& {' + $CommandList + '} *>&1')
$Result = $Instance.BeginInvoke()
$Instance.EndInvoke($Result) # Returns output merged across all streams.
Since output objects from streams other than the success stream (1) have a uniform type that reflects the stream of origin, you can examine the type of each output object to infer what stream it came from -
see this answer for details.
For more information about PowerShell's 6 output streams, run Get-Help about_Redirection.

Powershell Script output to variable - capture Write-Host output

Using this script: https://github.com/byterogues/powershell-bittrex-api
which I call from another script.
e.g.
$order = .\bittrex-api.ps1 -action buylimit -market BTC-TX -quantity 1 -rate 0.00011300
bittrex-api.ps1 catches an error and shows this on screen
BITTREX ERROR: API Query returned an error.
Error Message: MIN_TRADE_REQUIREMENT_NOT_MET
How can I capture the output from bittrex-api.ps1 into a variable so I can use this variable in my base script?
You can't. The script uses Write-Host to output the error. Write-Host only writes text to the console, it doesn't return any objects which means there's nothing to capture.
I would recommend modifying the script to use other cmdlets like Write-Error, Write-Output or any other Write-* which outputs to a stream (which you can redirect to the stdout-stream and save).
See http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/
To complement Frode F.'s helpful answer, which sensibly recommends modifying the script to use Write-Error for error reporting:
If modifying the code is not an option and you're using PSv5+, you can capture Write-Host output, because since PSv5 Write-Host writes to the newly introduced information output stream (to which primarily Write-Information is designed to write):
PowerShell's output streams are numbered, and the information stream has number 6 so that output-redirection expression 6>&1 redirects the information stream into the success output stream, whose number is 1, allowing regular capturing in a variable, as the following example shows:
# PSv5+
$captured = Write-Host 'write-host output' 6>&1
$captured # output what was captured -> 'write-host output'
To learn more about PowerShell's output streams and redirection, run Get-Help about_Redirection
Note:
Write-Host output captured via 6>&1 consists of one or more System.Management.Automation.InformationRecord instances, which print as if they were strings, namely by their .MessageData.Message property value, which is the string content of the argument(s) passed to Write-Host.
Therefore, any coloring that stems from the use of the -ForegroundColor and -BackgroundColor parameters is not (directly) passed through:
However, the information is preserved, namely in the .MessageData.ForegroundColor and .MessageData.BackgroundColor properties, along with the information about whether -NoNewLine was passed to Write-Host, in Boolean property .MessageData.NoNewLine
This answer shows how to recreate the original coloring from the captured objects.
By contrast, coloring via ANSI / VT escape sequences embedded in the original string argument(s) is preserved.
Note: To capture the output and also pass it through (to the success output stream), you have two options:
Simply enclose the statement in (...), but note that output will only show after all output has been collected.
($captured = Write-Host 'write-host output' 6>&1)
For streaming pass-through, use Tee-Object with the -Variable parameter:
Write-Host 'write-host output' 6>&1 | Tee-Object -Variable captured
The above techniques generally work for capturing and passing success-stream output through, irrespective of whether redirections are involved.