A simple example:
# Launch two jobs that each output two numbers,
# collect all their output, and clean up once they terminate.
" 1, 2 ", " 3, 4 " |
ForEach-Object { Start-Job -ScriptBlock ([scriptblock]::Create($_)) } |
Receive-Job -Wait -AutoRemoveJob
Note how the input command strings (designed to output two numbers each) must be converted to script blocks via [scriptblock]::Create() so that they can be passed to Start-Job's -ScriptBlock parameter.
However, it is preferable and simpler to provide the commands as script block to begin with; using script-block literals ({ ... }):
{ 1, 2 }, { 3, 4 } |
ForEach-Object { Start-Job $_ } |
Receive-Job -Wait -AutoRemoveJob
If you want to separate the timing of creating the jobs from waiting for their completion / collecting their output:
$jobs = { 1, 2 }, { 3, 4 } | ForEach-Object { Start-Job $_ }
# ... perform other tasks
$jobs | Receive-Job -Wait -AutoRemoveJob
All variants launch two jobs and (directly or indirectly) pipe the job objects output by Start-Job to Receive-Job, which collects their output (see bottom section for details):
-Wait additionally waits for both jobs to complete, thereby ensuring that all job output has been received and output
- Without
-Wait, Receive-Job collects all output produced so far, and returns right away, so that multiple calls may be necessary; optionally, you can collect output without consuming it (-Keep).
-AutoRemoveJob automatically cleans up the jobs afterwards, which by definition have terminated by then.
Note that there's also:
Wait-Job which performs waiting only, optionally for a given state (-State), and optionally with a timeout (-Timeout), necessitating a separate Receive-Job call to collect output.
Remove-Job which removes jobs only, optionally including their forceful termination (-Force), if they haven't completed yet.
How Receive-Job collects output:
Jobs never create direct output to the caller's console - all output, even to output streams other than the success stream, such as with Write-Host - are collected by Receive-Job and routed to the corresponding output streams of the caller.
Objects emitted by jobs to the success output stream (i.e. data output) are decorated with properties identifying the job of origin:
To illustrate the points above:
# This collects 42 in $dataOutput, and prints
# 'Some status message' to the display; you could suppress that with 6>$null
$dataOutput =
Start-Job { Write-Host 'Some status message'; 42 } |
Receive-Job -Wait -AutoRemoveJob
# Use ConvertTo-Json to surface the additional properties the output
# object is decorated with; you'll see something like:
# {
# "value": 42,
# "PSComputerName": "localhost",
# "RunspaceId": "ba1c0710-cb55-4759-81a2-2b089edd92a7",
# "PSShowComputerName": false,
# "PSSourceJobInstanceId": "44422412-6bc2-4b0d-8379-ddd09b2d8e56"
# }
$dataOutput | ConvertTo-Json
If you want to collect and emit output for each job separately:
$jobs = { 1, 2 }, { 3, 4 } | ForEach-Object { Start-Job $_ }
# Wait for all jobs to complete.
$jobs | Wait-Job
# Collect the output for each job separately.
# You'll see the following:
# VERBOSE: Output from job { 1, 2 }
# 1
# 2
# VERBOSE: Output from job { 3, 4 }
# 3
# 4
$jobs | ForEach-Object {
Write-Verbose -Verbose "Output from job { $($_.Command) }"
$_ | Receive-Job -Wait -AutoRemoveJob
}