我使用下面的代码来显示 PowerShell 作业的结果,超时时间为 120 秒。我想通过合并
Write-Progress
(基于已完成的作业数量)来增强此代码。我尝试使用这个示例作为参考,但是,当我尝试合并该代码时,进度条会在之后短暂显示所有作业都已完成。
$Jobs = @()
$ForceStoppedIds = @{}
$Jobs += Get-Job
$Jobs | Wait-Job -Timeout 120 | Out-Null
$Jobs | ?{$_.State -eq 'Running'} | Stop-Job -PassThru | %{$ForceStoppedIds[$_.Id] = $true}
foreach ($Job in $Jobs) {
$Name = $Job.Name
$Output = (Get-Job -Name $Name | Receive-Job)
if ($ForceStoppedIds.Contains($Job.Id)) {
Write-Output "$($Name) - Device unable to process request within 2 minutes"
} else {
Write-Output $Output
}
}
Wait-Job -Timeout 120
将阻塞线程,直到指定的超时或所有作业完成,因此不可能同时显示进度并等待它们。
我可以想到两种替代方案,第一个是围绕此 cmdlet 创建一个代理命令/代理函数来扩展其功能。
这些博客演示了如何做到这一点:
您还可以按照这个有用的答案中的指示进行操作。
另一种选择是定义您自己的函数,该函数执行与
Wait-Job
类似的工作,但是您可以添加一个基于 2 个条件运行的循环,而不是阻塞线程:
Diagnostics.Stopwatch
来实现)。$jobs
List<T>
仍然填充)。注意,在大多数情况下,下面的功能应该可以工作,但纯粹出于演示目的,不应依赖。
首先我们定义一个新函数,可用于显示进度并根据超时等待我们的作业:
using namespace System.Collections.Generic
using namespace System.Diagnostics
using namespace System.Threading
using namespace System.Management.Automation
function Wait-JobWithProgress {
[cmdletbinding()]
param(
[parameter(Mandatory, ValueFromPipeline)]
[object[]] $InputObject,
[parameter()]
[ValidateRange(1, [double]::MaxValue)]
[double] $TimeOut # In seconds!
)
begin {
$jobs = [List[object]]::new()
if ($withTimeOut = $PSBoundParameters.ContainsKey('TimeOut')) {
$span = [timespan]::FromSeconds($TimeOut)
}
}
process {
$jobs.AddRange($InputObject)
}
end {
$timer = [Stopwatch]::StartNew()
$total = $jobs.Count
$completed = 0.1
while ($jobs.Count) {
if ($withTimeOut -and $timer.Elapsed -gt $span) {
break
}
$remaining = $total - $completed
$average = $timer.Elapsed.TotalSeconds / $completed
$estimate = [math]::Round($remaining * $average)
$status = 'Completed Jobs: {0:0} of {1}' -f $completed, $total
$progress = @{
Activity = 'Waiting for Jobs'
PercentComplete = $completed / $total * 100
Status = $status
SecondsRemaining = $estimate
}
Write-Progress @progress
$id = [WaitHandle]::WaitAny($jobs.Finished, 200)
if ($id -eq [WaitHandle]::WaitTimeout) {
continue
}
# output this job
$jobs[$id]
# remove this job
$jobs.RemoveAt($id)
$completed++
}
# Stop the jobs not yet Completed and remove them
$jobs | Stop-Job -PassThru | ForEach-Object {
Remove-Job -Job $_
'Job [#{0} - {1}] did not complete on time and was removed.' -f $_.Id, $_.Name
} | Write-Warning
Write-Progress @progress -Completed
}
}
然后为了测试它,我们可以使用随机计时器创建一些作业:
0..10 | ForEach-Object {
Start-Job {
Start-Sleep (Get-Random -Minimum 5 -Maximum 15)
[pscustomobject]@{
Job = $using:_
Result = 'Hello from [Job #{0:D2}]' -f $using:_
}
}
} | Wait-JobWithProgress -TimeOut 10 |
Receive-Job -AutoRemoveJob -Wait | Format-Table -AutoSize