Fehlerbehandlung in PowerShell: ErrorAction, try, catch, finally


    Tags: ,

    Fehlermeldung in PowerShellEntwickler sollten sicher­stellen, dass Scripts beim Auf­treten von Fehlern nicht ab­rupt ab­brechen, sondern kon­trolliert enden. Benutzer­freund­licher Code vers­chont zudem User vor tech­nischen Fehler­meldungen. PowerShell bietet die Mittel für ein differen­ziertes Error-Handling.

    Grundsätzlich kann ein defensiver Programmier­stil ein Script in vielen Situationen vor dem Scheitern bewahren. Daher sollten bestimmte Voraus­setzungen nicht einfach als gegeben erachtet werden. Beispielsweise ist es meist ratsam, die Existenz von Dateien oder Rechnern im Netz zu prüfen, bevor man darauf zugreift. Auch eine erforderliche Version von PowerShell kann man abfragen.

    Zwei Typen von Fehlern

    Allerdings lassen sich nicht alle Bedin­gungen vorhersehen, unter denen ein Script ausgeführt wird. Entsprechend können Fehler auftreten, auf die der Code nicht vorbereitet ist. Aber in dieser Situation helfen Mechanismen für die Fehler­behandlung, die Ausführung geordnet zu beenden.

    PowerShell kennt grundsätzlich zwei Arten von Fehlern: Solche, die zum sofortigen Abbruch eines Kommandos oder Scripts führen ("Terminating Errors") und solche, die eine Fortsetzung erlauben ("Non-Terminating Errors").

    Die Anweisung mit Get-ChildItem wird trotz eines zuvor aufgetretenen Fehlers ausgeführt.

    Möchte man etwa Ereignisse in eine Log-Datei auf einem Laufwerk schreiben, das nicht existiert, dann gibt PowerShell standardmäßig den ent­sprechenden Fehler in roter Schrift auf dem Bildschirm aus und fährt mit der nächsten Anweisung fort.

    Hingegen führen beispiels­weise syntaktische Fehler im Code zum Abbruch des Scripts. Vergisst man etwa die schließende Klammer in einem Anweisungs­block oder ein Anführungs­zeichen am Ende eines Strings, dann tritt dieser Fall ein.

    Behandlung von nicht-terminierenden Fehlern

    Für beide Typen von Fehlern bietet PowerShell eigene Mechanismen, um das Verhalten von Scripts zu steuern. Die Möglich­keiten bei nicht-terminierenden Fehlern reichen vom Ignorieren und dem Unterdrücken der Meldungen bis hin zu einer Eskalation, so dass sie sich wie terminierende Fehler verhalten.

    Im einfachsten Fall teilt man einem Cmdlet über den Parameter ErrorAction mit, wie es auf solche Situationen reagieren soll. Mögliche Werte dafür sind:

    • SilentlyContinue: Die Fehlermeldung wird unterdrückt und PowerShell fährt mit der Ausführung des Codes fort
    • Ignore (seit Version 3): Der Fehler wird ignoriert und taucht nicht im Error-Stream auf.
    • Continue: Dabei handelt es sich um das Standard­verhalten. Fehler­meldungen werden (in roter Schrift) ausgegeben und das Script setzt seine Ausführung fort.
    • Stop: Erzwingt ein Verhalten wie bei einem terminierenden Fehler, die Ausführung wird also abgebrochen.
    • Inquire: Fragt den Benutzer, ob er die Ausführung fortsetzen möchte.

    Ein Aufruf könnte so aussehen:

    gci -r -force -include *.tmp -ErrorAction SilentlyContinue $env:USERPROFILE

    In diesem Beispiel würde keine Fehler­meldung ausgegeben, wenn der Benutzer, in dessen Kontext das Kommando läuft, keinen Zugriff auf einzelne Unter­verzeichnisse seines Profils hat.

    Mit dem Wert SilentlyContinue für ErrorAction ließen sich diese Fehlermeldungen unterdrücken.

    Reaktion über $ErrorActionPreference steuern

    Möchte man das Verhalten von PowerShell bei Fehlern nicht nur für einen Befehl, sondern für alle nachfolgenden Kommandos ändern, dann bietet sich dafür die Variable $ErrorActionPreference an. Ihr weist man einen der oben erläuterten Werte zu, der Wert Ignore wird dabei aber nicht unterstützt:

    $ErrorActionPreference = "SilentlyContinue"

    In diesem Beispiel würden die darauf folgenden Anweisungen bei einem Fehler keine Meldung auf dem Bildschirm ausgeben. PowerShell speichert sie aber in dem Array $error, so dass man sie nachträglich inspizieren kann.

    Variable $error auslesen

    So ließen sich für alle aufgetretenen Fehler die Eigenschaften CategoryInfo und Exception auf diese Weise anzeigen.

    $error | %{$_ | select CategoryInfo, Exception | fl}

    Die Zahl der gespeicherten Fehler ist identisch mit der Länge des Arrays und lässt sich mithin so abfragen:

    $error.Count

    Terminierende Fehler abfangen mit try/catch

    Die genannten Optionen für ErrorAction und $ErrorActionPreference haben keinen Einfluss auf das Verhalten bei terminierenden Fehlern. Möchte man verhindern, dass diese zum unkon­trollierten Ende eines Scripts führen, dann fängt man sie in einem try/catch-Konstrukt ab.

    Ein Syntaxfehler führt zum sofortigen Abbruch eines Scripts.

    Dieses ist folgendermaßen aufgebaut:

    Der catch-Block dient meistens für irgend­welche "Aufräumarbeiten", beispiels­weise um Änderungen vor dem Ende des Scripts rückgängig zu machen.

    PowerShell erlaubt die Verwendung mehrerer catch-Blöcke, wobei dann jeder für einen anderen Typ von Exception zuständig ist. In diesem Fall muss man den Namen der jeweiligen Ausnahme in eckigen Klammern angeben:

    Der hier benötigte Name der Exception lässt sich aus der $error-Variable auslesen, für den ersten Fehler im Speicher ginge das so:

    $error[0].Exception.GetType().FullName

    Nicht-terminierende Fehler lassen sich über einen catch-Block abfangen, wenn man die ErrorActionPreference auf Stop setzt.

    Wie erwähnt, fangen catch-Blöcke normalerweise keine nicht-terminierenden Fehler ab. Das kann man jedoch ändern, indem man $ErrorActionPreference auf Stop setzt.

    Täglich Know-how für IT-Pros mit unserem Newsletter

    Wir ver­wenden Ihre Mail-Adresse nur für den Ver­sand der News­letter.
    Es erfolgt keine per­sonen­be­zogene Auswertung.

    Bild von Wolfgang Sommergut
    Wolfgang Sommergut hat lang­jährige Erfahrung als Fach­autor, Berater und Kon­ferenz­sprecher zu ver­schie­denen Themen der IT. Da­ne­ben war er als System­ad­mi­ni­stra­tor und Con­sultant tätig.
    // Kontakt: E-Mail, XING, LinkedIn //

    Verwandte Beiträge

    Weitere Links

    1 Kommentar

    Wenn innerhalb eines Konstruktors festgestellt wird, dass die übergebenen Parameter logisch als nicht korrekt einzustufen sind (d. h. kein Absturz oder über try/catch aufgefangen), wie kann der Konstruktor die laufende Instanziierung beenden und das Ergebnis dem Aufrufer zur Kenntnis bringen (es gibt ja kein "return $null o. Ä.)? Das Ergebnis der Instanziierung soll also "keine Instanziierung" sein.