线程仍然使用 -asjob 和等待作业执行吗?

问题描述

大家好,下午好!

我有一个关于 -asjobinvoke-command 一起运行的快速问题。 如果我使用 Invoke-Command 运行 2 个 -asjob,当我尝试接收输出时它是否同时运行?这是否意味着 wait-job 会等到指定的第一个作业完成运行才能获得下一个结果?

Write-Host "Searching for PST and OST files.  Please be patient!" -BackgroundColor White -ForegroundColor DarkBlue
$pSTlocation = Invoke-Command -ComputerName localhost -ScriptBlock {Get-Childitem "C:\" -Recurse -Filter "*.pst" -ErrorAction SilentlyContinue | % {Write-Host $_.FullName,$_.lastwritetime}} -AsJob
$OSTlocation = Invoke-Command -ComputerName localhost -ScriptBlock {Get-Childitem "C:\Users\me\APpdata"  -Recurse -Filter "*.ost" -ErrorAction SilentlyContinue | % {Write-Host $_.FullName,$_.lastwritetime} } -AsJob


$pSTlocation | Wait-Job | Receive-Job 
$OSTlocation | Wait-Job | Receive-Job 

另外,另一个问题:我可以将作业的输出保存到一个变量而不显示在控制台上吗?我试图让它检查是否有任何回报,如果有输出,但如果没有做其他事情。

我试过了:

$job1 = $pSTlocation | Wait-Job | Receive-Job 
    if(!$job1){write-host "PST Found: $job1"} else{ "No PST Found"}   

$job2 = $OSTlocation | Wait-Job | Receive-Job 
    if(!$job2){write-host "OST Found: $job2"} else{ "No OST Found"}    

不走运,它输出以下内容

enter image description here

enter image description here

解决方法

注意:这个答案直接回答问题 - 请参阅the other answer;相反,它显示了一个可重用的习惯用法,用于以非阻塞方式等待多个作业完成。


以下示例代码使用基于子进程Start-Job cmdlet 创建本地作业,但该解决方案同样适用于本地线程 基于 Start-ThreadJob 创建的作业以及基于 远程 执行 Invoke-Command -ComputerName ... -AsJob 命令的作业,如问题中所用。

它显示了一个可重用的习惯用法,用于以非阻塞方式等待多个作业完成,允许在等待时进行其他活动,以及收集数组中的每个作业输出。

此处,仅在每个作业完成后收集输出,但请注意,在可用时分块收集它也是一种选择,使用(可能多个)Receive-Job 调用甚至在工作完成之前。

# Start two jobs,which run in parallel,and store the objects
# representing them in array $jobs.
# Replace the Start-Job calls with your 
#   Invoke-Command -ComputerName ... -AsJob
# calls.
$jobs = (Start-Job { Get-Date; sleep 1 }),(Start-Job { Get-Date '1970-01-01'; sleep 2 })

# Initialize a helper array to keep track of which jobs haven't finished yet.
$remainingJobs = $jobs

# Wait iteratively *without blocking* until any job finishes and receive and 
# output its output,until all jobs have finished.
# Collect all results in $jobResults.
$jobResults = 
  while ($remainingJobs) {

    # Check if at least 1 job has terminated.
    if ($finishedJob = $remainingJobs | Where State -in Completed,Failed,Stopped,Disconnected | Select -First 1) {

      # Output the just-finished job's results as part of custom object
      # that also contains the original command and the 
      # specific termination state.
      [pscustomobject] @{
        Job    = $finishedJob.Command
        State  = $finishedJob.State
        Result = $finishedJob | Receive-Job
      }

      # Remove the just-finished job from the array of remaining ones...
      $remainingJobs = @($remainingJobs) -ne $finishedJob
      # ... and also as a job managed by PowerShell.
      Remove-Job $finishedJob

    } else {

      # Do other things...
      Write-Host . -NoNewline
      Start-Sleep -Milliseconds 500

    }

  }

# Output the jobs' results
$jobResults

注意:

  • 尝试 $remainingJobs | Wait-Job -Any -Timeout 0 暂时检查是否终止任何一项作业而不阻止执行是很诱人的,但是从 PowerShell 7.1 开始,这不起作用预期:即使已经完成的作业也永远不会返回 - 这似乎是 bug,在 GitHub issue #14675 中讨论过。
,

如果我使用 -asjob 运行 2 个 Invoke-Command,它是否在我尝试接收输出时同时运行?

是的,PowerShell jobs 始终并行运行,无论它们是否远程执行,就像您的情况一样(使用 Invoke-Command -AsJob,假设问题中的 localhost 只是不同计算机实际名称的占位符)或本地(使用 Start-JobStart-ThreadJob)。

但是,通过使用(单独的)Wait-Job 调用,您同步等待每个作业完成(也以固定顺序)。也就是说,每个 Wait-Job 调用都会阻止进一步的执行,直到目标作业终止[1]

但是请注意,两个作业会在您等待第一个作业完成时继续执行。

如果您不想以阻塞方式等待,而是希望在等待两个作业完成的同时执行其他操作,则需要另一种方法,详见 the other answer

我可以将作业的输出保存到一个变量而不显示在控制台上吗?

是的,但问题是在您远程执行的脚本块 ({ ... }) 中,您错误地使用了 Write-Host 来尝试输出数据

Write-Host is typically the wrong tool to use,除非意图是将仅写入显示器,绕过 success output stream 并具有将输出发送到其他命令的能力,将其捕获在变量,或将其重定向到文件。要输出一个值,请单独使用;例如,$value 而不是 Write-Host $value(或使用 Write-Output $value,尽管很少需要);见this answer

因此,您在变量中收集作业输出的尝试失败了,因为 Write-Host 输出绕过变量赋值捕获并转到的成功输出流直接联系主机(控制台):

# Because the job's script block uses Write-Host,its output goes to the *console*,# and nothing is captured in $job1
$job1 = $pSTlocation | Wait-Job | Receive-Job 

(顺便说一句,该命令可以简化为 $job1 = $pSTlocation | Receive-Job -Wait).


[1] 请注意,Wait-Job 有一个可选的 -Timeout 参数,它允许您将等待时间限制为最多给定的秒数,并且如果目标作业没有输出则返回而不输出还没完。但是,从 PowerShell 7.1 开始,-Timeout 0 用于非阻塞轮询作业是否已完成不起作用 - 请参阅GitHub issue #14675