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

MKA*_*NET 2 powershell start-job powershell-jobs progress-bar

我使用下面的代码来显示 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
    
        }
    
    }
Run Code Online (Sandbox Code Playgroud)

San*_*zon 7

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

我能想到有两种选择,第一个是创建代理命令/代理函数以扩展其功能。

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

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


另一种选择是定义您自己的函数,该函数执行类似的工作Wait-Job,但是您可以添加一个基于两个条件运行的循环,而不是阻塞线程:

  • 经过的时间低于或等于我们作为参数传递给函数的超时(我们可以使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后为了测试它,我们可以使用随机计时器创建一些作业:

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
Run Code Online (Sandbox Code Playgroud)