如何 运行 两个 Start-Job 并等待他们的结果?

How to run two Start-Job and wait for their results?

我有一个命令列表,我正在尝试分别使用 Start-Job 来调用它们。每个命令都应该 return 一个对象数组。我如何等待每个命令的数组并在所有命令完成后组合它们 运行?我正在使用 PS 5.1

cmds = "Command-One", "Command-Two"

foreach cmd in cmds 
{
    Start-Job -ScriptBlock { cmd }
}

一个简单的例子:

# 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

请注意输入命令 strings(设计为每个输出两个数字)必须如何转换为 script blocks via [scriptblock]::Create() so that they can be passed to Start-Job-ScriptBlock 参数。

但是,最好也更简单地以脚本块的形式提供命令;使用 script-block 文字 ({ ... }):

{ 1, 2 }, { 3, 4 } | 
  ForEach-Object { Start-Job $_ } | 
    Receive-Job -Wait -AutoRemoveJob

如果您想将创建作业的时间与等待它们完成/收集它们的输出分开:

$jobs = { 1, 2 }, { 3, 4 } | ForEach-Object { Start-Job $_ } 
 
# ... perform other tasks

$jobs | Receive-Job -Wait -AutoRemoveJob

所有变体启动两个作业并(直接或间接)通过管道传输 Start-Job to Receive-Job 输出的作业对象,后者收集它们的输出(详情请参阅底部部分):

  • -Wait 额外等待两个作业完成,从而确保已接收到所有作业输出并输出

    • 如果没有 -WaitReceive-Job 收集到目前为止 产生的所有输出 ,并立即收集 returns,因此可能需要多次调用;可选地,您可以收集输出而不使用它 (-Keep)。
  • -AutoRemoveJob 之后自动清理作业,根据定义,这些作业届时已终止。

注意还有:

  • Wait-Job 仅执行等待,可选地针对给定的 state (-State),并且可选地使用 timeout (-Timeout),需要单独的 Receive-Job 调用来收集输出。

  • Remove-Job 仅删除作业,如果作业尚未完成,可选择包括强制终止 (-Force)。


Receive-Job如何收集输出:

  • 作业永远不会直接输出到调用者的控制台 - 所有输出,甚至是 output streams other than the success stream, such as with Write-Host - 都由 Receive-Job 收集并路由到调用者的相应输出流。

  • 作业向 success 输出流(即 data 输出)发出的对象是 装饰有标识工作来源的属性:

    • 包含 auto-generated 标识作业的 GUID 的属性。

      • .RunspaceId
      • .PSSourceJobInstanceId(仅当作业为 运行 时,看起来很简单)
      • 两者都会在每次调用时发生变化;请注意,您可以通过 -Name 传递给 Start-Job 的可选 self-chosen 符号名称在此处反映为 而不是 ,因此没有明显的关联方式输出对象及其来自的工作 - 见下文。
    • PowerShell 的 远程处理(其基础设施后台作业部分使用)主要感兴趣的另外两个属性:

      • .PSComputerName:总是 'localhost' 在后台作业中
      • .PSShowComputerName:总是在后台作业中 $false;提示格式化系统是否显示 .PSComputerName 属性.
      • 的值

为了说明以上几点:

# 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

如果您想分别为每个作业收集和发出输出:

$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
}