Reputation: 55
I'm starting a job and I need to receive the output before the job finishes. It uses write-host instead of write-output, I know when I use write-output this shouldn't be a problem but assume I need to use write-host.
So as an example I have a job
$job = Start-Job -Name $taskId -ScriptBlock { Write-host "This is the job output, there is a keyword"; start-sleep 2 }
I need to retrieve the console output and check for a keyword there, so I use receive-job
$result = Receive-Job -Job $job -Keep -ErrorAction SilentlyContinue *>&1
I use *>&1 to be able to capture every output. But I need to suppress the console output and only assign the output to the variable. Unfortunately I cannot suppress the console log. Using Out-Null also does not populate the variable.
Is there a way to do this?
Thanks
Upvotes: 2
Views: 84
Reputation: 439777
What you're seeing is a long-standing bug, still present as of PowerShell 7.5.x, reported a long time ago in GitHub issue #3354 and GitHub issue #9585; in a nutshell:
In background jobs (but not thread jobs - see next point), PowerShell remoting calls and mini-shells (from-PowerShell CLI calls that use a script block to specify code to execute), (unless silenced at the source) output streams 3
(warning stream, 4
(verbose stream), 5
(debug stream), and, except in mini-shells, 6
(information stream, to which Write-Host
prints) cannot be suppressed (silenced) with >
, because they are locally replicated as host output, which is invariably printed.
As you've observed, the problem also surfaces when you use*>&1
, i.e. a redirection that routes all other streams through stream 1
, the success output stream.
An easy way to bypass the bug is to use Start-ThreadJob
rather than Start-Job
; use of Start-ThreadJob
is generally preferable anyway, because it creates comparatively lightweight, much faster thread-based jobs (whereas Start-Job
uses child
processes) that support full type fidelity.[1]
Start-ThreadJob
comes with PowerShell (Core) 7, and in Windows PowerShell can be installed on demand, using, e.g., Install-Module ThreadJob -Scope CurrentUser
.
See the bottom section for a Start-ThreadJob
solution.
Otherwise, the only workarounds are:
Use *>&1
redirections from inside your job, as shown in sirtao's helpful answer.
If modifying the job script block is not an option, the only way to avoid the bug is to refrain from using Receive-Job
:
Santiago's helpful answer offers a solution for reading (non-redirected) Write-Host
/ Write-Information
output, specifically, via the .Information
collection on the child jobs.
If you need the output from other streams too, apply the technique analogously to the properties representing them, i.e. .Output
, .Error
, .Warning
, and .Debug
(e.g., $job.ChildJobs[0].Output
); note, however, that you'll need to access them separately and individually.
Start-ThreadJob
-based solution:
# Note the use of Start-ThreadJob instead of Start-Job.
$job = Start-ThreadJob -ScriptBlock {
Write-host "This is the job output, there is a keyword"
}
# Wait for the job to complete and retrieve all output via
# the success output stream (*>&1)
# Thanks to Start-ThreadJob, *no* to-host output is now printed.
$allOutput = $job | Receive-Job -Wait -AutoRemoveJob *>&1
# Print the captured output.
"`$allOutput value: [$allOutput]"
[1] See the bottom section of this answer more information.
Upvotes: 2
Reputation: 60838
You may be able to capture the host output from the Job by inspecting the .Information
property of the child job. In this case you can also use Receive-Job
however the Job host output will still be console output.
$job = Start-Job -Name $taskId -ScriptBlock {
Write-Host 'This is the job output, there is a keyword'
Start-Sleep 2
}
$job | Wait-Job | Out-Null
$hostOutput = $job.ChildJobs | ForEach-Object { $_.Information.ReadAll() }
Upvotes: 2
Reputation: 2880
You need to redirect the write-host
inside the scriptblock.
To do that, you need to redirect the InformationStream(6) to the SuccessStream(1).
# this prints nothing.
$job = Start-Job -Name $taskId -ScriptBlock { Write-host "This is the job output, there is a keyword" 6>&1; start-sleep 2 }
# this, too, prints nothing.
$result = Receive-Job -Job $job -Keep
# this prints "This is the job output, there is a keyword"
$result
Mind you: this means $result
is a PSObject in the SuccessStream.
Any decoration like colors would be lost.
Is this just playing to understand how Write-Host
works\its limits or there is some specific need?
Upvotes: 1