Как установить ограничение по времени для сценария PowerShell, на котором будет выполняться? - PullRequest
5 голосов
/ 11 июня 2010

Я хочу установить ограничение времени для скрипта PowerShell (v2), чтобы он принудительно завершался после истечения этого срока.

Я вижу, что в PHP есть команды, такие как set_time_limit и max_execution_time, где вы можете ограничить продолжительность выполнения скрипта и даже функции.

С моим сценарием цикл do / while, который смотрит на время, не подходит, так как я вызываю внешнюю библиотеку кода, которая может просто зависать в течение длительного времени.

Я хочу ограничить блок кода и разрешить ему запускаться только в течение x секунд, после чего я прерву этот блок кода и верну ответ пользователю, для которого истек срок действия сценария.

Я просмотрел фоновые задания, но они работают в другом потоке, поэтому не будут иметь прав на уничтожение родительского потока.

Кто-нибудь имел дело с этим или есть решение?

Спасибо!

Ответы [ 6 ]

3 голосов
/ 01 марта 2011

Нечто подобное тоже должно работать ...

$job = Start-Job -Name "Job1" -ScriptBlock {Do {"Something"} Until ($False)}
Start-Sleep -s 10
Stop-Job $job
1 голос
/ 22 февраля 2014

Я знаю, что это старый пост, но я использовал это в своих сценариях.

Я не уверен, правильно ли он используется, но System.Timers.Timer, который Джордж выдвинул, дал мне идею, и она, кажется, работает для меня.

Я использую его для серверов, которые иногда зависают при запросе WMI, тайм-аут останавливает его зависание. Вместо write-host я выводил сообщение в файл журнала, чтобы видеть, какие серверы сломаны, и исправлять их при необходимости.

Я также не использую 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
1 голос
/ 11 июня 2010

Вот пример использования таймера.Я не пробовал это лично, но я думаю, что это должно работать:

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 голосов
/ 10 апреля 2019

Я придумал этот сценарий.

  • Start-Transcript для записи всех действий и сохранения их в файл.
  • Сохраните значение текущего идентификатора процесса в переменной $ p, затем запишите его на экран.
  • Назначить текущую дату переменной $ startTime.
  • После этого я назначаю его снова и добавляю дополнительное время к текущей дате к действительному значению переменной $ $.
  • Функция 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
0 голосов
/ 17 сентября 2018

Вот мое решение, вдохновленное этим сообщением в блоге .Он завершит работу, когда все будет выполнено или закончится время (в зависимости от того, что произойдет раньше).

Я помещаю материал, который хочу выполнить в течение ограниченного времени, в функцию:

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 (включая пример того, как получить вывод из задания):

#... 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 голосов
/ 14 августа 2018

Примерно так:

## 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 и формат ЧЧ: мм, поскольку это не облегчает использованиедней в примере.

...