如何设置PowerShell脚本的运行时间限制?

如何设置PowerShell脚本的运行时间限制?,powershell,time,multithreading,limit,powershell-2.0,Powershell,Time,Multithreading,Limit,Powershell 2.0,我想对PowerShell(v2)脚本设置一个时间限制,以便它在该时间限制过期后强制退出 我看到在PHP中,它们有像set_time_limit和max_execution_time这样的命令,您可以在其中限制脚本甚至函数可以执行的时间 在我的脚本中,查看时间的do/while循环是不合适的,因为我调用的外部代码库可能会挂起很长时间 我想限制一个代码块,只允许它运行x秒,之后我将终止该代码块,并向用户返回脚本超时的响应 我已经看过后台作业,但它们在不同的线程中运行,因此对父线程没有终止权限 有人

我想对PowerShell(v2)脚本设置一个时间限制,以便它在该时间限制过期后强制退出

我看到在PHP中,它们有像set_time_limit和max_execution_time这样的命令,您可以在其中限制脚本甚至函数可以执行的时间

在我的脚本中,查看时间的do/while循环是不合适的,因为我调用的外部代码库可能会挂起很长时间

我想限制一个代码块,只允许它运行x秒,之后我将终止该代码块,并向用户返回脚本超时的响应

我已经看过后台作业,但它们在不同的线程中运行,因此对父线程没有终止权限

有人处理过这个问题或有解决办法吗


谢谢

下面是一个使用计时器的示例。我没有亲自尝试过,但我认为它应该有效:

function Main
{
    # do main logic here
}

function Stop-Script
{
    Write-Host "Called Stop-Script."
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.CloseAsync()
}

$elapsedEventHandler = {
    param ([System.Object]$sender, [System.Timers.ElapsedEventArgs]$e)

    Write-Host "Event handler invoked."
    ($sender -as [System.Timers.Timer]).Stop()
    Unregister-Event -SourceIdentifier Timer.Elapsed
    Stop-Script
}

$timer = New-Object System.Timers.Timer -ArgumentList 2000 # setup the timer to fire the elapsed event after 2 seconds
Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier Timer.Elapsed -Action $elapsedEventHandler
$timer.Start()

Main

像这样的东西也应该有用

$job=Start job-Name“Job1”-ScriptBlock{Do{“Something”}Until($False)}
开始睡眠-s10
停止工作$Job

我知道这是一篇老文章,但我在脚本中使用了它

我不确定它是否正确使用,但乔治提出的System.Timers.Timer给了我一个想法,它似乎对我有用

我将它用于有时挂起WMI查询的服务器,超时会阻止它被卡住。 我不再写主机,而是将消息输出到一个日志文件,这样我就可以看到哪些服务器坏了,并在需要时修复它们

我也不使用guid,而是使用服务器主机名

我希望这是有意义的,对你有帮助

$MyScript = {
              Get-WmiObject -ComputerName MyComputer -Class win32_operatingsystem
            }

$JobGUID = [system.Guid]::NewGuid()

$elapsedEventHandler = {
    param ([System.Object]$sender, [System.Timers.ElapsedEventArgs]$e)

    ($sender -as [System.Timers.Timer]).Stop()
    Unregister-Event -SourceIdentifier $JobGUID
    Write-Host "Job $JobGUID removed by force as it exceeded timeout!"
    Get-Job -Name $JobGUID | Remove-Job -Force
}

$timer = New-Object System.Timers.Timer -ArgumentList 3000 #just change the timeout here
Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action $elapsedEventHandler -SourceIdentifier $JobGUID
$timer.Start()

Start-Job -ScriptBlock $MyScript -Name $JobGUID

像这样的怎么样:

## SET YOUR TIME LIMIT
## IN THIS EXAMPLE 1 MINUTE, BUT YOU CAN ALSO USE HOURS/DAYS
# $TimeSpan = New-TimeSpan -Days 1 -Hours 2 -Minutes 30
$TimeSpan = New-TimeSpan -Minutes 1
$EndTime = (Get-Date).AddMinutes($TimeSpan.TotalMinutes).ToString("HH:mm")

## START TIMED LOOP
cls
do
{
## START YOUR SCRIPT
Write-Warning "Test-Job 1...2...3..."
Start-Sleep 3
Write-Warning "End Time = $EndTime`n"
}
until ($EndTime -eq (Get-Date -Format HH:mm))

## TIME REACHED AND END SCRIPT
Write-Host "End Time reached!" -ForegroundColor Green
使用小时或天作为计时器时,请确保调整$TimeSpan.TotalMinutes
以及HH:mm格式,因为这不便于在示例中使用天。

这是我的解决方案,灵感来自。当所有操作都已执行或时间用完(以先发生的为准)时,它将完成运行

我将要在有限时间内执行的内容放在函数中:

function WhatIWannaDo($param1, $param2)
{
    # Do something... that maybe takes some time?
    Write-Output "Look at my nice params : $param1, $param2"
}
function Limit-JobWithTime($Job, $TimeInSeconds, $RetryInterval=5)
{
    try
    {
        $timer = [Diagnostics.Stopwatch]::StartNew()

        while (($timer.Elapsed.TotalSeconds -lt $TimeInSeconds) -and ('Running' -eq $job.JobStateInfo.State)) {
            $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
            $tsString = $("{0:hh}:{0:mm}:{0:ss}" -f [timespan]::fromseconds($totalSecs))
            Write-Progress "Still waiting for action $($Job.Name) to complete after [$tsString] ..."
            Start-Sleep -Seconds ([math]::Min($RetryInterval, [System.Int32]($TimeInSeconds-$totalSecs)))
        }
        $timer.Stop()
        $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
        $tsString = $("{0:hh}:{0:mm}:{0:ss}" -f [timespan]::fromseconds($totalSecs))
        if ($timer.Elapsed.TotalSeconds -gt $TimeInSeconds -and ('Running' -eq $job.JobStateInfo.State)) {
            Stop-Job $job
            Write-Verbose "Action $($Job.Name) did not complete before timeout period of $tsString."

        } else {
            if('Failed' -eq $job.JobStateInfo.State){
                $err = $job.ChildJobs[0].Error
                $reason = $job.ChildJobs[0].JobStateInfo.Reason.Message
                Write-Error "Job $($Job.Name) failed after with the following Error and Reason: $err, $reason"
            }
            else{
                Write-Verbose "Action $($Job.Name) completed before timeout period. job ran: $tsString."
            }
        }        
    }
    catch
    {
    Write-Error $_.Exception.Message
    }
}
#... maybe some stuff before?
$job = Start-Job -Name PrettyName -Scriptblock ${function:WhatIWannaDo} -argumentlist @("1st param", "2nd param")
Limit-JobWithTime $job -TimeInSeconds 60
Write-Verbose "Output from $($Job.Name): "
$output = (Receive-Job -Keep -Job $job)
$output | %{Write-Verbose "> $_"}
#... maybe some stuff after?
我还有另一个功能,可以跟踪计时器,如果一切都已完成执行:

function WhatIWannaDo($param1, $param2)
{
    # Do something... that maybe takes some time?
    Write-Output "Look at my nice params : $param1, $param2"
}
function Limit-JobWithTime($Job, $TimeInSeconds, $RetryInterval=5)
{
    try
    {
        $timer = [Diagnostics.Stopwatch]::StartNew()

        while (($timer.Elapsed.TotalSeconds -lt $TimeInSeconds) -and ('Running' -eq $job.JobStateInfo.State)) {
            $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
            $tsString = $("{0:hh}:{0:mm}:{0:ss}" -f [timespan]::fromseconds($totalSecs))
            Write-Progress "Still waiting for action $($Job.Name) to complete after [$tsString] ..."
            Start-Sleep -Seconds ([math]::Min($RetryInterval, [System.Int32]($TimeInSeconds-$totalSecs)))
        }
        $timer.Stop()
        $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
        $tsString = $("{0:hh}:{0:mm}:{0:ss}" -f [timespan]::fromseconds($totalSecs))
        if ($timer.Elapsed.TotalSeconds -gt $TimeInSeconds -and ('Running' -eq $job.JobStateInfo.State)) {
            Stop-Job $job
            Write-Verbose "Action $($Job.Name) did not complete before timeout period of $tsString."

        } else {
            if('Failed' -eq $job.JobStateInfo.State){
                $err = $job.ChildJobs[0].Error
                $reason = $job.ChildJobs[0].JobStateInfo.Reason.Message
                Write-Error "Job $($Job.Name) failed after with the following Error and Reason: $err, $reason"
            }
            else{
                Write-Verbose "Action $($Job.Name) completed before timeout period. job ran: $tsString."
            }
        }        
    }
    catch
    {
    Write-Error $_.Exception.Message
    }
}
#... maybe some stuff before?
$job = Start-Job -Name PrettyName -Scriptblock ${function:WhatIWannaDo} -argumentlist @("1st param", "2nd param")
Limit-JobWithTime $job -TimeInSeconds 60
Write-Verbose "Output from $($Job.Name): "
$output = (Receive-Job -Keep -Job $job)
$output | %{Write-Verbose "> $_"}
#... maybe some stuff after?
。。。最后,我启动函数
WhatIWannaDo
作为后台作业,并将其传递给
limitjobwithtime
(包括如何从作业中获取输出的示例):

function WhatIWannaDo($param1, $param2)
{
    # Do something... that maybe takes some time?
    Write-Output "Look at my nice params : $param1, $param2"
}
function Limit-JobWithTime($Job, $TimeInSeconds, $RetryInterval=5)
{
    try
    {
        $timer = [Diagnostics.Stopwatch]::StartNew()

        while (($timer.Elapsed.TotalSeconds -lt $TimeInSeconds) -and ('Running' -eq $job.JobStateInfo.State)) {
            $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
            $tsString = $("{0:hh}:{0:mm}:{0:ss}" -f [timespan]::fromseconds($totalSecs))
            Write-Progress "Still waiting for action $($Job.Name) to complete after [$tsString] ..."
            Start-Sleep -Seconds ([math]::Min($RetryInterval, [System.Int32]($TimeInSeconds-$totalSecs)))
        }
        $timer.Stop()
        $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
        $tsString = $("{0:hh}:{0:mm}:{0:ss}" -f [timespan]::fromseconds($totalSecs))
        if ($timer.Elapsed.TotalSeconds -gt $TimeInSeconds -and ('Running' -eq $job.JobStateInfo.State)) {
            Stop-Job $job
            Write-Verbose "Action $($Job.Name) did not complete before timeout period of $tsString."

        } else {
            if('Failed' -eq $job.JobStateInfo.State){
                $err = $job.ChildJobs[0].Error
                $reason = $job.ChildJobs[0].JobStateInfo.Reason.Message
                Write-Error "Job $($Job.Name) failed after with the following Error and Reason: $err, $reason"
            }
            else{
                Write-Verbose "Action $($Job.Name) completed before timeout period. job ran: $tsString."
            }
        }        
    }
    catch
    {
    Write-Error $_.Exception.Message
    }
}
#... maybe some stuff before?
$job = Start-Job -Name PrettyName -Scriptblock ${function:WhatIWannaDo} -argumentlist @("1st param", "2nd param")
Limit-JobWithTime $job -TimeInSeconds 60
Write-Verbose "Output from $($Job.Name): "
$output = (Receive-Job -Keep -Job $job)
$output | %{Write-Verbose "> $_"}
#... maybe some stuff after?

我想出了这个剧本

  • 启动Transcript以记录所有操作并将其保存到文件中
  • 将当前进程ID值存储在变量$p中,然后将其写入屏幕
  • 将当前日期分配给$startTime变量
  • 之后,我再次分配它,并将当前日期的额外时间添加到var$expiration
  • updateTime函数返回应用程序关闭前的剩余时间。并将其写入控制台
  • 如果计时器超过过期时间,则启动循环和终止进程
  • 就这样
代码:


这确实有一个问题,即它将等待到时间已过,即使程序已提前终止…您可以添加一个简短的描述吗?