将写入进度添加到获取作业/等待作业

问题描述 投票:0回答:1

我使用下面的代码来显示 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
    
        }
    
    }
powershell progress-bar start-job powershell-jobs
1个回答
7
投票

Wait-Job -Timeout 120
将阻塞线程,直到指定的超时或所有作业完成,因此不可能同时显示进度并等待它们。

我可以想到两种替代方案,第一个是围绕此 cmdlet 创建一个代理命令/代理函数来扩展其功能。

这些博客演示了如何做到这一点:

您还可以按照这个有用的答案中的指示进行操作。


另一种选择是定义您自己的函数,该函数执行与

Wait-Job
类似的工作,但是您可以添加一个基于 2 个条件运行的循环,而不是阻塞线程:

  • 经过的时间低于或等于我们作为参数传递给函数的超时(我们可以使用
    Diagnostics.Stopwatch
    来实现)。
  • 而且,作业仍在 Running
    $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
© www.soinside.com 2019 - 2024. All rights reserved.