我正在尝试获取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
)在新窗口中运行命令,该窗口在默认情况下也是异步的,即,串行启动的命令可以并行运行。在有限的形式中,这允许您实时监视特定于命令的输出,但随附了以下警告:
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/