When running code as a ThreadJob
in PowerShell 7.2.11 (also applies to 7.2.13), I've noticed that not all output is affected by the -Streaminghost
argument.
Start-ThreadJob {
Write-Host 'Hello'
} -Streaminghost $Host
will output Hello
,
but using Write-Output
in the thread will not show the output until I do a Get-Job | Receive-Job
...
Start-ThreadJob {
Write-Output 'Hello'
} -Streaminghost $Host
Start-ThreadJob {
'Hello'
} -Streaminghost $Host
What gives?
Santiago Squarzon has provided the crucial pointers:
The purpose of Start-ThreadJob
's -StreamingHost
parameter is to directly connect all output streams other than the success and the error output streams to the specified PowerShell host.
If you pass the automatic $Host
variable, i.e. the host of the current session, whatever is output to those streams then surfaces:
Behind the scenes, -StreamingHost
uses the .CreateRunspace(PSHost, InitialSessionState)
overload of the RunspaceFactory
class, as can be gleaned from the source code.
The success and the error stream outputs are quietly collected by the resulting job and require Receive-Job
to be retrieved on demand later.
Note: Whether or not you use -StreamingHost
, output objects from the other output streams are also collected in the job and therefore potentially reported by Receive-Job
, via their respective streams, though the output may not surface; as of PowerShell 7.4.6:
Write-Host
, Write-Warning
and Write-Debug
output is surfaced by Receive-Job
unconditionally, which is expected only for the former two, given that they produce output unconditionally; it is unclear why Write-Debug
, which by default produces no output (produces output only when requested via the common -Debug
parameter or when the $DebugPreference
preference variable is set to 'Continue'
), behaves the same.
Also surfacing Write-Verbose
and Write-Information
output requires passing the relevant common parameters to Receive-Object
, -Verbose
and -InformationAction Continue
and, in the case of Write-Verbose
only, requires having turned on verbose output inside the job too (via the common -Verbose
parameter or via $VerbosePreference = 'Continue'
). This is not necessary for Write-Information
which seemingly always produces output, which isn't surfaced by default, however.[1]
Receive-Job
in combination with thread jobs (Start-ThreadJob
) as well as with ForEach-Object -Parallel
doesn't honor the silence / non-silence of streams as set inside the job is problematic - see GitHub issue #24016 for a discussion.Start-Job
), at least with respect to to-host display, but capturing / redirecting stream output has its problems too - see GitHub issue #9585).If you truly want to surface all output streams from the thread job instantly, asynchronously, you can use the following technique, which uses redirection *>&1
, which merges all outputs streams into the success output stream, and pipes the result to Out-Host
for direct-to-host output:
$job = Start-ThreadJob {
& {
Write-Host 'Hello host'
Write-Output 'Hello success'
Write-Error 'Hello error'
} *>&1 | Out-Host
} -StreamingHost $Host
Note:
The above also outputs a (non-terminating) error in addition to host (Write-Host
) and success output (Write-Output
) to demonstrate that all output surfaces instantly, asynchronously.
Due to using Out-Host
for direct-to-display printing, you cannot capture any output with this technique.
[1] The following commands illustrate this difference: Write-Information info 6>&1
prints info
, because info
was still emitted and therefore redirected to the success output stream - even though the command by itself, without the redirection, is silent. By contrast, Write-Verbose verbose 4>&1
outputs nothing, because Write-Verbose
, in the absence of requesting verbose output (e.g., with -Verbose
), emits nothing