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

问题描述 投票:5回答:6

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

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

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

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

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

有没有人处理过此问题或有解决方案?

谢谢!

powershell time multithreading limit powershell-v2.0
6个回答
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
投票

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

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

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

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格式,因为这不利于在示例中使用days。


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(包括如何从Job获取输出的示例):

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

0
投票

我想出了这个剧本。

  • Start-Transcript记录所有操作并将其保存到文件中。
  • 将当前进程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
© www.soinside.com 2019 - 2024. All rights reserved.