1

Я запускаю задачу планировщика задач Windows:

Powershell 
-command C:\file.ps1  2>&1 > c:\test.log

file.ps1 содержит

Try{
    echo "a"
    sleep 5
    echo "b"
}
Finally{
    echo "c"
}

Когда я запускаю задачу вручную и ожидаю ее завершения, test.log содержит "abc". Но когда я прерываю его, вручную выходя из него (кнопка "Завершить"), он содержит только "а".

Я думал, Finally , намерен даже запустить при выходе из сценария PowerShell? Мой ожидаемый результат был "AC"

1 ответ1

3

Как упоминается в комментариях Lieven Keersmaekers, блок finally не получает шанса на запуск, когда вы используете End для прерывания задачи. Это потому, что Конец просто убивает процесс. PowerShell с удовольствием запустил бы блок finally, прежде чем делать другие важные действия, но PowerShell вообще ничего не может сделать после сигнала завершения процесса - он мертв. Ни один процесс не может справиться с этим. По словам Раймонда Чена:

TerminateProcess - это низкоуровневая функция уничтожения процессов. Он обходит DLL_PROCESS_DETACH и все остальное в процессе. Как только вы завершите работу с TerminateProcess, в этом процессе больше не будет кода пользовательского режима. Это ушло Не проходи иди. Не собирайте 200 долларов.

Однако есть обходной путь. Поскольку команда End в планировщике заданий не уничтожает подпроцессы процесса задания, ваш основной сценарий PowerShell может запустить сторожевой процесс для выполнения окончательной очистки. Этот скрипт может выглядеть примерно так (назовем его watchdog.ps1):

$watched = Get-Process -Id $args[0]
$watched.WaitForExit()
'Cleanup' >> c:\test.log

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

Тогда ваш основной скрипт может выглядеть так:

$myPid = [System.Diagnostics.Process]::GetCurrentProcess().Id
$watchdog = Start-Process 'powershell' "-c .\watchdog.ps1 $myPid" -PassThru -WindowStyle Hidden
'Starting'
sleep 10
'Finished'

В начале он запускает сторожевой скрипт, предоставляя свой собственный идентификатор процесса. Затем он идет своим обычным делом, выходя нормально, как только это будет сделано.

Если вы хотите, чтобы очистка сторожевого таймера происходила только в случае прерывания основного сценария, добавьте эту строку в конец основного сценария:

$watchdog.Kill()

Это предотвратит выход сторожа за пределы второй линии.

Ваше перенаправление будет применяться только к основному процессу, поэтому сторожевой таймер должен обрабатывать свое собственное направление вывода. В зависимости от конфигурации вашей запланированной задачи, вам может понадобиться указать полный путь к сценарию watchdog в команде watchdog-launching основного сценария.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .