powershell - 等待其他 session 中的多个同时Powershell命令完成,然后再运行下一个命令

标签 powershell

我正在尝试获取Powershell主脚本来执行以下操作:

  • 在其他3个powershell session 中运行命令(它们都运行大约1个小时-我希望它们可以同时运行,以便它们可以同时完成所有工作)
  • 等待所有3个其他powershell session 完成
  • 在初始powershell窗口中继续其余命令

  • 极其简单的例子

    我的实际用例与以下类似,不同之处在于时间总是变化,ECHO "hi"应该仅在所有其他(3)命令完成后才发生(在这种情况下,我们知道它们将花费10000秒,但是在我的实际用例中,差异很大)。还要注意,尚不清楚这3个命令中的哪个命令每次花费的时间最长。
    start powershell { TIMEOUT 2000 }
    start powershell { TIMEOUT 3000 }
    start powershell { TIMEOUT 10000 }
    ECHO "hi"
    

    我可以看到(here)可以在命令前面放置一个&,以便告诉powershell在完成后续命令之前要等到它完成为止。但是,我不知道如何使用3个同时命令

    最佳答案

    您确实在寻找background jobs建议的 Powershell Lee Daily

    但是,作业比较繁琐,因为每个作业都在其自己的进程中运行,这会引入大量开销,并且还可能导致类型保真度下降(由于涉及到PowerShell的基于XML的序列化基础结构,请参见this answer)。

    ThreadJob module提供了基于线程的轻量级替代方案。
    它随PowerShell [Core] v6 +一起提供,在Windows中可以按需安装PowerShell,例如Install-Module ThreadJob -Scope CurrentUser

    您只需调用Start-ThreadJob而不是Start-Job,并使用标准*-Job cmdlet来管理此类线程作业-与管理常规后台作业的方式相同。

    这是一个例子:

    $startedAt = [datetime]::UtcNow
    
    # Define the commands to run as [thread] jobs.
    $commands = { $n = 2; Start-Sleep $n; "I ran for $n secs." }, 
                { $n = 3; Start-Sleep $n; "I ran for $n secs." }, 
                { $n = 10; Start-Sleep $n; "I ran for $n secs." }
    
    # Start the (thread) jobs.
    # You could use `Start-Job` here, but that would be more resource-intensive
    # and make the script run considerably longer.
    $jobs = $commands | Foreach-Object { Start-ThreadJob $_ }
    
    # Wait until all jobs have completed, passing their output through as it
    # is received, and automatically clean up afterwards.
    $jobs | Receive-Job -Wait -AutoRemoveJob
    
    
    "All jobs completed. Total runtime in secs.: $(([datetime]::UtcNow - $startedAt).TotalSeconds)"
    

    上面的结果如下:请注意,单个命令的输出将在可用时报告,但是直到所有命令完成,调用脚本的执行才会继续:
    I ran for 2 secs.
    I ran for 3 secs.
    I ran for 10 secs.
    All jobs completed. Total runtime in secs.: 10.2504931
    

    注意:在这种简单的情况下,很明显哪个输出来自哪个命令,但是更典型的情况是各种作业的输出将以不可预测的方式交错运行,这使得难以解释输出-解决方案请参见下一节。

    如您所见,在后台为基于线程的并行执行引入的开销是最小的-总体执行只花了10秒多一点的时间,这是3个命令中运行时间最长的时间。

    如果要改用基于进程的Start-Job,则总体执行时间可能如下所示,这显示了引入的大量开销,尤其是第一次在 session 中运行后台作业时:
    All jobs completed. Total runtime in secs.: 18.7502717
    

    也就是说,至少在 session 的第一次调用中,后台并行执行的好处被否定了-在这种情况下,执行所需的时间比顺序执行所需的时间更长。

    尽管在同一 session 中后续的基于进程的后台作业运行速度更快,但开销仍然明显高于基于线程的作业。

    同步作业输出流

    如果要显示每个命令的后台命令输出,则需要分别收集输出。

    注意:在控制台窗口(终端)中,这要求您等待所有命令完成后才能显示输出(因为无法通过就地更新同时显示多个输出流,至少对于常规输出而言是这样)命令)。
    $startedAt = [datetime]::UtcNow
    
    $commands = { $n = 1; Start-Sleep $n; "I ran for $n secs." }, 
                { $n = 2; Start-Sleep $n; "I ran for $n secs." }, 
                { $n = 3; Start-Sleep $n; "I ran for $n secs." }
    
    $jobs = $commands | Foreach-Object { Start-ThreadJob $_ }
    
    # Wait until all jobs have completed.
    $null = Wait-Job $jobs
    
    # Collect the output individually for each job and print it.
    foreach ($job in $jobs) {
      "`n--- Output from {$($job.Command)}:"
      Receive-Job $job
    } 
    
    "`nAll jobs completed. Total runtime in secs.: $('{0:N2}' -f ([datetime]::UtcNow - $startedAt).TotalSeconds)"
    

    上面将打印如下内容:
    
    --- Output from { $n = 1; Start-Sleep $n; "I ran for $n secs." }:
    I ran for 1 secs.
    
    --- Output from { $n = 2; Start-Sleep $n; "I ran for $n secs." }:
    I ran for 2 secs.
    
    --- Output from { $n = 3; Start-Sleep $n; "I ran for $n secs." }:
    I ran for 3 secs.
    
    All jobs completed. Total runtime in secs.: 3.09
    

    使用Start-Process在单独的窗口中运行命令

    在Windows上,您可以使用Start-Process(别名为start)在新窗口中运行命令,该窗口在默认情况下也是异步的,即,串行启动的命令可以并行运行。

    在有限的形式中,这允许您实时监视特定于命令的输出,但随附了以下警告:
  • 您必须手动分别激活新窗 Eloquent 能看到正在生成的输出。
  • 仅在命令运行时,输出才可见;完成后,其窗口将自动关闭,因此您无法在事后检查输出。
  • 要变通解决此问题,您必须在PowerShell cmdlet中使用诸如Tee-Object之类的内容,以便还捕获文件中的输出,调用者以后可以检查该文件。
  • 这也是使输出以编程方式可用的唯一方法,尽管只能以文本形式提供。
  • 通过powershell.exe将PowerShell命令传递到Start-Process要求您以字符串(而不是脚本块)形式传递命令,并且具有令人讨厌的解析要求,例如需要转义"字符。作为\"(原文如此)-参见下文。
  • 最后但并非最不重要的一点是,使用Start-Process还引入了大量的处理开销(尽管对于运行时间很长的命令来说可能并不重要)。
  • $startedAt = [datetime]::UtcNow
    
    # Define the commands - of necessity - as *strings*.
    # Note the unexpected need to escape the embedded " chars. as \"
    $commands = '$n = 1; Start-Sleep $n; \"I ran for $n secs.\"',
                '$n = 2; Start-Sleep $n; \"I ran for $n secs.\"',
                '$n = 3; Start-Sleep $n; \"I ran for $n secs.\"'
    
    # Use `Start-Process` to launch the commands asynchronously,
    # in a new window each (Windows only).
    # `-PassThru` passes an object representing the newly created process through.
    $procs = $commands | ForEach-Object { Start-Process -PassThru powershell -Args '-c', $_ }
    
    # Wait for all processes to exit.
    $procs.WaitForExit()
    
    
    "`nAll processes completed. Total runtime in secs.: $('{0:N2}' -f ([datetime]::UtcNow - $startedAt).TotalSeconds)"
    

    关于powershell - 等待其他 session 中的多个同时Powershell命令完成,然后再运行下一个命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56612034/

    相关文章:

    azure - 为什么我收到 "New-AzSqlDatabaseImport : Not found: Entity not found to invoke import"

    powershell - 从 powershell v3.0 管理网络场

    python - 在一个 session 下使用 python 运行 powershell 脚本

    powershell - 具有约束的Powershell随机获取

    c# - 从 powershell 调用 api 时,空值来自 Post 方法(FromBody)

    powershell - 查找 "pwdLastSet"和当前日期/时间之间的差异

    powershell - 在工作流旁边使用递归函数

    powershell - 如何通过 scm.azurewebsites.net REST API 更新 settings.job?

    arrays - Powershell - 删除基于静态数组的多行文本?

    powershell - Powershell:如何从结果中排除标签