PowerShell-Block wird im Windows-Task-Scheduler schließlich nicht ausgeführt

406
Peter

Ich führe eine Windows Task Scheduler Task aus:

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

file.ps1 enthält

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

Wenn ich die Aufgabe manuell starte und warte, bis sie abgeschlossen ist, enthält das test.log "abc". Wenn ich es unterbreche, indem ich es manuell verlasse ("End" -Button), enthält es nur "a".

Ich dachte, Finallyes soll sogar laufen, wenn man ein Powershell-Skript verlässt? Mein erwartetes Ergebnis war "ac"

1
Das hängt immer von der Art der Kündigung ab. Beachten Sie auch die Hinweise zum Umgang mit Pipes in [about_Try_Catch_Finally] (https://docs.microsoft.com/de-de/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6&viewFallbackFrom=powershell-Micros. PowerShell.Core). Seth vor 6 Jahren 0
@Peter - wenn Sie ein C ++ - Programm gewaltsam beenden, während Sie es in try / finally ausführen, wird auch das final nicht ausgeführt. Lieven Keersmaekers vor 6 Jahren 1
Ist es dann irgendwie möglich, etwas auszuführen, wenn ein Powershell-Skript getötet wird? Peter vor 6 Jahren 0

1 Antwort auf die Frage

2
Ben N

Wie in den Kommentaren von Lieven Keersmaekers angedeutet, hat der finally-Block keine Chance, zu laufen, wenn Sie die Task mit End beenden. Dies liegt daran, dass Ende den Prozess gerade beendet. PowerShell würde gerne den finally-Block ausführen, bevor er seine anderen aufregenden Dinge erledigt, aber PowerShell kann nach dem Prozessbeendigungssignal nichts mehr tun - es ist tot. Es gibt keinen Weg für einen Prozess, um damit umzugehen. In den Worten von Raymond Chen :

TerminateProcess ist die einfache Prozessabbruchfunktion. Es umgeht DLL_PROCESS_DETACH und alles andere im Prozess. Sobald Sie mit TerminateProcess beendet haben, wird in diesem Prozess kein Code mehr im Benutzermodus ausgeführt. Es ist weg. Pass nicht weiter. Sammle nicht 200 $.

Es gibt jedoch eine Problemumgehung. Da der Befehl "Task Scheduler" den Unterprozess des Taskprozesses nicht beendet, kann Ihr PowerShell-Hauptskript einen Watchdog-Prozess starten, um eine endgültige Bereinigung durchzuführen. Dieses Skript könnte ungefähr so ​​aussehen (nennen wir es watchdog.ps1):

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

Es benötigt eine Prozess-ID, wartet, bis der Prozess beendet ist, und führt nur dann eine Bereinigung durch, das Äquivalent zu Ihrer endgültigen Blockierung.

Dann könnte Ihr Hauptskript so aussehen:

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

Zu Beginn startet es das Watchdog-Skript und stellt seine eigene Prozess-ID bereit. Dann geht es normal weiter, sobald es fertig ist.

Wenn die Bereinigung des Watchdog nur dann erfolgen soll, wenn das Hauptskript unterbrochen wird, fügen Sie diese Zeile am Ende des Hauptskripts hinzu:

$watchdog.Kill() 

Dadurch wird verhindert, dass der Watchdog über die zweite Zeile hinausgeht.

Ihre Umleitung gilt nur für den Hauptprozess. Der Watchdog muss also seine eigene Ausgaberichtung festlegen. Abhängig von der Konfiguration Ihrer geplanten Aufgabe müssen Sie möglicherweise den vollständigen Pfad zum watchdog-Skript im Befehl watchdog-launching des Hauptscripts angeben.