Zum Hauptinhalt springen

Unbehandelte Fehler abfangen mit Try-Catch-Finally

Im vorigen Abschnitt wurde der Unterschied zwischen terminierenden und behandelten Fehlern besprochen. Alle behandelten Fehler gehen ursprünglich auf einen unbehandelten Fehler zurück. Auch in Powershell ist es möglich, einen terminierenden Fehler aufzufangen und zu behandeln. Man spricht von Auffangen, weil ein Fehler normalerweise mit dem Befehl Throw geworfen wird. Um einen Fehler aufzufangen, muß der Befehl, der einen terminierenden Fehler auslösen kann, mit einem Try-Block umschlossen werden.

Try {
   TuWas # Der Befehl, der fehlschlagen kann
}
Catch {
   'Es ist ein Fehler aufgetreten!'   # Die Fehlerbehandlung
   $_.Exception.Message
}
Finally {
   'Einen Moment, ich räume auf'
   1..100 | Foreach-object {
      Write-Progress -Activity 'Cleanup' -PercentComplete $_
      Start-Sleep -Milliseconds 10
   }
}

Der Try-Block ist verpflichtend. In seinem Skriptblock geben Sie den Code an, der einen Fehler erzeugen kann. Hinter dem Try-Block kann ein Catch-Block folgen, der aber nur dann aufgerufen wird, wenn im Try-Block ein terminierender Fehler aufgetreten ist. Behandelte Fehler lösen den Catch-Block nicht aus! Als letztes kann ein Finally-Block angegeben werden. Der Code im Finally-Block wird immer ausgeführt, unabhängig davon, ob ein Fehler aufgetreten ist oder nicht. Der Finally-Block wird sogar dann ausgeführt, wenn Sie die Skript-Ausführung mit Strg-C abbrechen! Sie können einen Try-Block auch ausschließlich mit einem Finally-Block kombinieren.

Wenn Sie den obenstehenden Code ausführen, wird ein Fehler ausgelöst, da es sich bei TuWas weder um einen ausführbaren Befehl noch um einen String handelt - der Text ist ohne Anführungszeichen angegeben. Sobald ein Fehler ausgelöst wird, wird der Fehler in der $Error-Variablen gespeichert, der Try-Block abgebrochen und die Ausführung des Catch-Blocks beginnt – sofern ein Catch-Block existiert, denn Catch-Blöcke sind optional.

Innerhalb des Catch-Blocks kann beliebiger Code ausgeführt werden. Sie können hier also eine Routine einbauen, die den Fehler behebt, oder den Fehler ausgeben oder protokollieren. Powershell stellt das Fehler-Objekt dafür in der anonymen Variable $_ bereit. Wenn Sie nur den Text der Fehlermeldung ausgeben wollen, können Sie auf $_.Exception.Message zugreifen. Der Fehlertext wird dann nur noch als normaler Text ausgegeben, nicht als rote Fehlermeldung. 

Der Finally-Block ist ebenfalls optional. Wenn er vorhanden ist, wird er immer ausgeführt, unabhängig davon, ob ein Fehler aufgetreten ist oder nicht. Er kann z.B. dazu genutzt werden, Dateien wieder zu entfernen, die im Try- oder Catch-Block erzeugt worden sind, oder um Netzwerkverbindungen wieder zu beenden. Beachten Sie, dass der Finally-Block selbst dann ausgeführt wird, wenn Sie mit Strg+C das Abbrechen der Skriptverarbeitung erzwingen. Allerdings werden nach dem Abbruch keine Daten mehr in die Standardausgabe geschrieben, so dass Textausgaben aus dem Finally-Block nicht mehr ausgegeben werden.

Try
{
   get-date | Out-File C:\temp\Try.txt
   Read-Host -Prompt 'Bitte geben Sie etwas ein'
}
Finally
{
   Write-Host 'Did Something'
   Remove-Item -Path C:\temp\try.txt -Force
}
> Bitte geben Sie etwas ein: Did Something

Im diesem Beispiel sehen Sie die Ausgabe des Finally-Blocks nach Abbruch durch Strg+C. Sie bekommen dank des Write-Host die Ausgabe "Did Something" angezeigt. Wird Write-Host entfernt, und nur der Text 'Did something' ausgegeben, scheint Finally nichts zu tun. Sie können allerdings an der Datei Try.txt erkennen, dass die Datei im Finally-Block wieder gelöscht wird. Die Ausgabe mit Write-Host dient nur zu Demonstrationszwecken und macht aus technischer Sicht keinen Sinn!

Prinzipiell können Sie Try mit Catch und Finally beliebig kombinieren, also den Finally wie im vorigen Beispiel nur mit Try verwenden, oder den Try nur mit einem Catch-Block nutzen, oder alle drei Blöcke zusammen zum Einsatz bringen. Der Catch-Block kann aber noch mehr, denn er kann sogar mehrfach verwendet werden, um auf spezifische Fehler mit einem eigenen Catch-Block zu reagieren. Alles, was Sie dafür tun müssen, ist den Fehlertyp hinter den Catch-Befehl zu schreiben.

Im nächsten Beispiel wird ein Kopiervorgang in einen Try-Block eingeschlossen. Wenn der Zielordner nicht existiert, wird eine DirectoryNotFoundException ausgelöst, die direkt vom ersten Catch-Block abgefangen wird. Handelt es sich um einen anderen Fehler, wird der zweite, unspezifische Catch-Block aufgerufen. Wurde ein Catch-Block ausgelöst, werden die übrigen Catch-Blöcke ignoriert.

Try {
   copy C:\Windows\WindowsUpdate.log -Destination $env:temp\win.log
}
Catch [DirectoryNotFoundException] {
   Write-Error -Message 'Das Zielverzeichnis existiert nicht'
}
Catch {
   Write-Error -Message 'Allgemeiner Fehler'
}