Powershell 最终块未在 Windows 任务计划程序中执行

Powershell 最终块未在 Windows 任务计划程序中执行

我正在运行 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”。但是当我通过手动退出(“结束”按钮)来中断它时,它只包含“a”。

我以为Finally退出 powershell 脚本时也会运行?我预期的结果是“ac”

答案1

正如 Lieven Keersmaekers 在评论中提到的,当您使用 End 中断任务时,finally 块没有机会运行。这是因为 End 只是终止了进程。PowerShell 很乐意在执行其他退出操作之前运行 finally 块,但 PowerShell 在进程终止信号之后根本无法执行任何操作 — 它已经死了。任何进程都无法处理这种情况。在Raymond Chen 的话

TerminateProcess 是低级进程终止函数。它绕过 DLL_PROCESS_DETACH 和进程中的任何其他内容。使用 TerminateProcess 终止后,该进程中将不再运行用户模式代码。它消失了。不要通过。不要收取 200 美元。

不过,有一个解决方法。由于任务计划程序的 End 命令不会终止任务进程的子进程,因此主 PowerShell 脚本可以启动监视进程来执行任何最终清理。该脚本可能看起来像这样(我们称之为watchdog.ps1):

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

它需要一个进程 ID,等待直到该进程退出,然后才进行一些清理,相当于 finally 块。

那么你的主脚本可能看起来像这样:

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

一开始,它会启动看门狗脚本,并提供自己的进程 ID。然后它会执行正常操作,完成后正常退出。

如果你希望看门狗进行清理仅有的如果主脚本被中断,请将此行添加到主脚本末尾:

$watchdog.Kill()

此举将阻止监管机构超越其第二道防线。

您的重定向仅适用于主进程,因此看门狗必须处理自己的输出方向。根据您计划的任务的配置,您可能需要在主脚本的 watchdog-launching 命令中输入看门狗脚本的完整路径。

相关内容