Наконец, блок Powershell не выполняется в планировщике задач Windows.

416
Peter

Я запускаю задачу планировщика задач 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
Это всегда зависит от вида прекращения. Также обратите внимание на упоминание об обработке канала в [about_Try_Catch_Finally] (https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6&viewFallbackFrom=powershell-Microsoft. PowerShell.Core). Seth 6 лет назад 0
@Peter - если вы принудительно убили программу на C ++ во время выполнения в try / finally, команда finally также не будет выполнена. Lieven Keersmaekers 6 лет назад 1
Тогда возможно ли как-то выполнить что-то, когда скрипт powershell будет убит? Peter 6 лет назад 0

1 ответ на вопрос

2
Ben N

Как упоминается в комментариях 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 основного сценария.

Похожие вопросы