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

时间:2010-06-11 03:07:36

标签: powershell time multithreading limit powershell-v2.0

我想在PowerShell(v2)脚本上设置一个时间限制,以便在该时间限制到期后强行退出。

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

使用我的脚本,查看时间的do / while循环是不合适的,因为我正在调用一个可以挂起很长时间的外部代码库。

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

我查看了后台作业,但是它们在不同的线程中运行,因此不会对父线程具有终止权限。

是否有人处理此问题或有解决方案?

谢谢!

6 个答案:

答案 0 :(得分:3)

这样的事也应该有用......

$job = Start-Job -Name "Job1" -ScriptBlock {Do {"Something"} Until ($False)}
Start-Sleep -s 10
Stop-Job $job

答案 1 :(得分:1)

以下是使用Timer的示例。我没有亲自试过,但我认为它应该有效:

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

答案 2 :(得分:1)

我知道这是一篇旧帖子,但我在我的剧本中使用了这个帖子。

我不确定它是否正确使用它,但George提出的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

答案 3 :(得分:0)

怎么样呢?

## 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格式,因为这在示例中不方便使用天。

答案 4 :(得分:0)

这是我受this blog post启发的解决方案。完成所有操作或时间用完(以先发生的为准)时,它将结束运行。

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

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

...,最后我将函数WhatIWannaDo作为后台作业启动,并将其传递给Limit-JobWithTime(包括如何从作业中获取输出的示例):< / strong>

#... 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?

答案 5 :(得分:0)

我想出了这个脚本。

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

代码:

Start-Transcript C:\Transcriptlog-Cleanup.txt #write log to this location
$p = Get-Process  -Id $pid | select -Expand id  # -Expand selcts the string from the object id out of the current proces.
Write-Host $p

$startTime = (Get-Date) # set start time
$startTime
$expiration = (Get-Date).AddSeconds(20) #program expires at this time
# you could change the expiration time by changing (Get-Date).AddSeconds(20) to (Get-Date).AddMinutes(10)or to hours whatever you like

#-----------------
#Timer update function setup
function UpdateTime
   {
    $LeftMinutes =   ($expiration) - (Get-Date) | Select -Expand minutes  # sets minutes left to left time
    $LeftSeconds =   ($expiration) - (Get-Date) | Select -Expand seconds  # sets seconds left to left time


    #Write time to console
    Write-Host "------------------------------------------------------------------" 
    Write-Host "Timer started at     :  "  $startTime
    Write-Host "Current time         :  "  (Get-Date)
    Write-Host "Timer ends at        :  "  $expiration
    Write-Host "Time on expire timer : "$LeftMinutes "Minutes" $LeftSeconds "Seconds"
    Write-Host "------------------------------------------------------------------" 
    }
#-----------------


do{   #start loop
    Write-Host "Working"#start doing other script stuff
    Start-Sleep -Milliseconds 5000  #add delay to reduce spam and processing power
    UpdateTime #call upadate function to print time
 }
until ($p.HasExited -or (Get-Date) -gt $expiration) #check exit time

Write-Host "done"
Stop-Transcript
if (-not $p.HasExited) { Stop-Process -ID $p -PassThru } # kill process after time expires