我正在运行 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 命令中输入看门狗脚本的完整路径。