Question:
I am having to run a Microsoft cmdlet, and the important bit of information is written to console using a Write-Host line within the cmdlet.
It is NOT returned, so I cannot do $result = Commandlet …
A different value is returned that is not of use to me, what I actually need is printed to console within the commandlet is there anyway I can ‘sniff’ or ‘scrape’ the console to get the information I want?
1 2 |
$result = Test-Cluser |
Test-Cluster will print stuff like: ‘HadUnselectedTests’, ‘ClusterConditionallyApproved’, etc.
But the value it returns in the path to the .htm report file.
And the .htm report file does not contain one of those status codes unfortunately so I cannot just parse the .htm file for it either.
Any suggestions?
Answer:
Note: As for why you should never use Write-Host
to output data, see this answer.
In PSv5+:
1 2 |
$result = Test-Cluster 6>&1 |
Since version 5, Write-Host
writes to the newly introduced information stream, whose number is 6
.
6>&1
redirects that stream to the success output stream (number 1
), so that it too can be captured in $result
.
Caveat: The related Out-Host
cmdlet does not write to the information stream; its output cannot be captured – see this answer for the differences between Write-Host
and Out-Host
.
In PSv4-:
There is no way to capture Write-Host
output in-session.
The only workaround is to launch another instance of Powershell with the target command specified as a string.
Caveats:
- Such an invocation is slow,
- prevents passing of arguments with their original data type
- invariably only returns string data (lines of text)
- returns output from all output streams, including error output
- for a list of all output streams, see
Get-Help about_Redirection
- for a list of all output streams, see
1 2 |
$result = powershell -noprofile -command 'Test-Cluster' |
Note that using a script block to pass the command (-command { Test-Cluster }
) would not work, because PowerShell then uses serialization and deserialization to emulate the in-session behavior.
Optional reading: output streams in PowerShell and how to redirect them:
Get-Help about_Redirection
discusses a list of all output streams, which can be targeted by their numbers; since PSv5, these are:
1 2 3 4 5 6 7 |
1 ... success output stream (implicit output and Write-Output output) 2 ... error output stream (Write-Error and unhandled errors) 3 ... warnings (Write-Warning) 4 ... verbose output (Write-Verbose) 5 ... debug output (Write-Debug) 6 ... (v5+) Write-Information and Write-Host output |
Note that some streams are silent by default and require opt-in to produce output, either via a preference variable (e.g., $VerbosePreference
) or a common parameter (e.g., -Verbose
)
{n}>
allows redirecting the number{n}
stream; if{n}
is omitted,1
is implied:- to a file (e.g.,
3> c:/tmp/warnings.txt
- to “nowhere”, i.e suppressing the output (e.g.,
3> $null
) - to the success output stream (e.g.,
3>&1
); note: only stream1
can be targeted this way.
- to a file (e.g.,
*>
targets all output streams.
Note: Unlike in POSIX-like shells (e.g., bash
), the order of multiple redirection expression does not matter.
Therefore, the following POSIX-like shell idiom – which redirects error output to the success stream and silences only the original success output – does NOT work:
1 2 |
... 2>&1 1>$null # !! NO output in PowerShell |
To achieve this in PowerShell, you mustn’t redirect 1
and instead filter the objects in the success by their stream of origin.
Case in point: In the end, the OP wanted the following: capture only warning output, without the regular (success) output:
1 2 |
Test-Cluster 3>&1 | Where-Object { $_ -is [System.Management.Automation.WarningRecord] } |
Objects that came from the warning stream have type [System.Management.Automation.WarningRecord]
, which is what enables the filtering above.