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.
$Host
variable, i.e. the host of the current session, whatever is output to those streams then surfaces:
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.
[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