将写入过程添加到Get-Job/Wait-Job

发布于 2025-01-22 08:56:12 字数 918 浏览 0 评论 0 原文

我正在使用以下代码以120秒的超时显示PowerShell作业的结果。我想通过合并 Write-Progress (基于完成的作业数)来增强此代码。我尝试使用此示例 ,当我尝试合并该代码时,Progress Bar在之后简要显示所有作业都已完成。

    $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
    
        }
    
    }

I'm using the below code to display the results of PowerShell Jobs with a timeout of 120 seconds. I would like to enhance this code by incorporating Write-Progress (based on number of jobs completed). I tried using this example as a reference, however, when I try to incorporate that code, the progress bar displays briefly after all the jobs are all done already.

    $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
    
        }
    
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

请远离我 2025-01-29 08:56:12

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

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

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

您还可以从


另一种选择是定义自己的功能,该功能执行类似的工作,例如 wait-job ,但是,您可以添加一个基于2个条件运行的循环,而不是阻止线程

  • :低于或等于我们作为参数传递给函数的超时(我们可以使用 diagnostics.stopwatch 为此)。
  • 而且,工作仍然是运行 $ obs

注意下面的功能在大多数情况下应起作用,但是纯粹仅用于演示目的,不应依靠。

首先,我们定义一个可以用于显示进度的新功能,并根据超时等待我们的作业:

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

Wait-Job -Timeout 120 will block the thread until the specified timeout or all jobs have completed, hence, is not possible to display progress and wait for them at the same time.

There are 2 alternatives that I can think of, the first one would be to create a proxy command / proxy function around this cmdlet to extend it's functionality.

These blogs demonstrate how to do it:

You can also follow the indications from this helpful answer.


The other alternative is to define your own function that does a similar work as Wait-Job but, instead of blocking the thread, you can add a loop that will run based on 2 conditions:

  • That the elapsed time is lower than or equal to the Timeout we passed as argument to the function (we can use Diagnostics.Stopwatch for this).
  • And, that the jobs are still Running (the $jobs List<T> is still populated).

Note, the function below should work in most cases however is purely for demonstration purposes only and should not be relied upon.

First we define a new function that can be used to display progress as well as wait for our jobs based on a timeout:

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
    }
}

Then for testing it, we can create a few jobs with a random timer:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文