Welcome to Our Website

Alles, was Sie über Ausnahmen wissen wollten

  • 23.05.2020
  • 14 Minuten zum Lesen
    • j
    • f
    • s

Fehlerbehandlung ist nur ein Teil des Lebens, wenn es darum geht, Code zu schreiben. Wir können oft überprüfen und validateconditions für erwartetes Verhalten. Wenn das Unerwartete passiert, wenden wir uns der Ausnahmebehandlung zu., Sie können problemlos Ausnahmen behandeln, die durch den Code anderer Personen generiert werden, oder Sie können Ihre eigenen Ausnahmen generieren, die andere verarbeiten können.

Hinweis

Die Originalversion dieses Artikels erschien auf dem Blog von @KevinMarquette. ThePowerShell Team dankt Kevin für den Austausch dieser Inhalte mit uns. Bitte schauen Sie sich seinen Blog an atPowerShellExplained.com.

Grundlegende Terminologie

Wir müssen einige grundlegende Begriffe behandeln, bevor wir in diese einsteigen.

Ausnahme

Eine Ausnahme ist wie ein Ereignis, das erstellt wird, wenn die normale Fehlerbehandlung das Problem nicht beheben kann.,Der Versuch, eine Zahl durch Null zu teilen oder der Speicher wird knapp, sind Beispiele für etwas, das anexception erzeugt. Manchmal erstellt der Autor des von Ihnen verwendeten Codes Ausnahmen für bestimmte Probleme, wenn sie auftreten.

Werfen und Fangen

Wenn eine Ausnahme auftritt, sagen wir, dass eine Ausnahme ausgelöst wird. Um eine ausgelöste Ausnahme zu behandeln, muss youneed sie abfangen. Wenn eine Ausnahme ausgelöst wird und nicht von etwas abgefangen wird, stoppt das Skript die Ausführung.

Der Aufrufstapel

Der Aufrufstapel ist die Liste der Funktionen, die sich gegenseitig aufgerufen haben., Wenn eine Funktion aufgerufen wird, wird sie dem Stapel oder dem Anfang der Liste hinzugefügt. Wenn die Funktion beendet wird oder zurückkehrt, wird sie entferntaus dem Stapel.

Wenn eine Ausnahme ausgelöst wird, wird dieser Aufrufstapel überprüft, damit ein Ausnahmebehandler eingreift.

Fehler beenden und nicht beenden

Eine Ausnahme ist im Allgemeinen ein Abschlussfehler. Eine ausgelöste Ausnahme wird entweder abgefangen oder beendet die aktuelle Ausführung. Standardmäßig wird ein nicht abschließender Fehler von Write-Errorgeneriert und fügt dem Ausgabestream einen Fehler hinzu, ohne eine Ausnahme auszulösen.,

Ich weise darauf hin, weil Write-Error und andere nicht abschließende Fehler nicht diecatchauslösen.

Schlucken einer Ausnahme

Dies ist, wenn Sie einen Fehler abfangen, nur um ihn zu unterdrücken. Tun Sie dies mit Vorsicht, da dies Probleme beim Roubleshooting sehr schwierig machen kann.

Grundlegende Befehlssyntax

Hier ist ein kurzer Überblick über die grundlegende Syntax für die Ausnahmebehandlung in PowerShell.

Throw

Um ein eigenes Ausnahmeereignis zu erstellen, werfen wir eine Ausnahme mit dem Schlüsselwort throw.,

function Start-Something{ throw "Bad thing happened"}

Dies erzeugt eine Laufzeitausnahme, die ein Abschlussfehler ist. Es wird von einer catch in acalling oder beendet das Skript mit einer solchen Nachricht.

Write-Error-ErrorAction Stop

Ich habe erwähnt, dass Write-Error standardmäßig keinen Abschlussfehler auslöst. Wenn Sie-ErrorAction Stop angeben, generiert Write-Erroreinen Abschlussfehler, der mit einercatchbehandelt werden kann.,

Write-Error -Message "Houston, we have a problem." -ErrorAction Stop

Vielen Dank an Lee Dailey für die Erinnerung an die Verwendung von -ErrorAction Stop auf diese Weise.

Cmdlet-ErrorAction Stop

Wenn Sie -ErrorAction Stop für eine erweiterte Funktion oder ein Cmdlet angeben, werden alle Write-Error – Anweisungen in abschließende Fehler umgewandelt, die die Ausführung stoppen oder von einer catchbehandelt werden können.,

Start-Something -ErrorAction Stop

Try/Catch

Die Art und Weise, wie die Ausnahmebehandlung in PowerShell (und vielen anderen Sprachen) funktioniert, ist, dass Sie zuerst try als Teil des Codes und wenn es einen Fehler auslöst, können Sie catch es. Hier ist eine kurze Probe.

Dascatch Skript wird nur ausgeführt, wenn ein Abschlussfehler vorliegt. Wenn die try korrekt ausgeführt wird, dannes überspringt die catch.,

Try/Finally –

Manchmal brauchen Sie nicht, um einen Fehler zu behandeln, müssen jedoch noch einige code auszuführen, wenn eine exceptionhappens oder nicht. Einfinally Skript macht genau das.

Schauen Sie sich dieses Beispiel an:

Wenn Sie eine Ressource öffnen oder eine Verbindung herstellen, sollten Sie sie schließen. Wenn dieExecuteNonQuery() eine Ausnahme auslöst, wird die Verbindung nicht geschlossen. Hier ist der gleiche code in ein try/finally block.

In diesem Beispiel wird die Verbindung bei einem Fehler geschlossen. Es ist auch geschlossen, wenn es noerror gibt., Das Skript finally wird jedes Mal ausgeführt.

Da Sie die Ausnahme nicht abfangen, wird sie immer noch im Aufrufstapel weitergegeben.

Try / Catch / Finally

Es ist vollkommen gültig, catch und finally zusammen zu verwenden. Die meiste Zeit werden Sie das eine oder verwendendie andere, aber Sie können Szenarien finden, in denen Sie beide verwenden.

$PSItem

Nachdem wir die Grundlagen aus dem Weg geräumt haben, können wir etwas tiefer graben.,

Imcatch – Block befindet sich eine automatische Variable ($PSItem oder$_) vom TypErrorRecord, die die Details zur Ausnahme enthält. Hier ist ein kurzer überblick über einige der keyproperties.

Für diese Beispiele habe ich einen ungültigen Pfad in ReadAllText, um diese Ausnahme zu generieren.

::ReadAllText( '\\test\no\filefound.log')

PSItem.toString ()

Dies gibt Ihnen die sauberste Nachricht für die Protokollierung und allgemeine Ausgabe., ToString() wird automatisch aufgerufen, wenn $PSItem in einem String platziert ist.

catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}

$PSItem.InvocationInfo

Diese Eigenschaft enthält zusätzliche Informationen, die von PowerShell über die Funktion oder das Skript gesammelt wurden, in dem die Ausnahme ausgelöst wurde. Hier ist die InvocationInfo aus der Beispielausnahme, die Icreated.

Die wichtigen Details hier zeigen die ScriptName, die Line des Codes und die ScriptLineNumberwo der Aufruf gestartet wurde.

$PSItem.,ScriptStackTrace

Diese Eigenschaft zeigt die Reihenfolge der Funktionsaufrufe an, die Sie zu dem Code geführt haben, in dem die Ausnahme generiert wurde.

Ich rufe nur Funktionen im selben Skript auf, aber dies würde die Aufrufe verfolgen, wenn Multiplescripts beteiligt wären.

$PSItem.Ausnahme

Dies ist die eigentliche Ausnahme, die ausgelöst wurde.

$PSItem.Ausnahme.Nachricht

Dies ist die allgemeine Nachricht, die die Ausnahme beschreibt und ein guter Ausgangspunkt ist, wenntroubleshooting. Die meisten Ausnahmen haben eine Standardnachricht, können aber auch auf etwas Benutzerdefiniertes gesetzt werden, wenn die Ausnahme ausgelöst wird.,

PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."

Dies ist auch die Meldung, die beim Aufruf von $PSItem.ToString() zurückgegeben wird, wenn auf derErrorRecordkein Satz vorhanden war.

$PSItem.Ausnahme.InnerException

Ausnahmen können innere Ausnahmen enthalten. Dies ist oft der Fall, wenn der Code, den Sie aufrufenfängt eine Ausnahme und löst eine andere Ausnahme aus. Die ursprüngliche Ausnahme wird darin platziertdie neue Ausnahme.

PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.

Ich werde dies später noch einmal wiederholen, wenn ich über das erneute Auslösen von Ausnahmen spreche.

$PSItem.Ausnahme.,StackTrace

Dies ist die StackTrace für die Ausnahme. Ich habe oben eine ScriptStackTrace, aber diese ist für die Aufrufe von verwaltetem Code.

at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)at CallSite.Target(Closure , CallSite , Type , String )

Sie erhalten diesen Stack-Trace nur, wenn das Ereignis aus verwaltetem Code ausgelöst wird. Ich rufe ein .NETFramework funktioniert direkt, so dass wir in diesem Beispiel nur sehen können. Wenn Sie sich einen Stack-Trace ansehen, suchen Sie im Allgemeinen, wo Ihr Code stoppt und die Systemaufrufe beginnen.

Arbeiten mit Ausnahmen

Ausnahmen haben mehr als die grundlegenden Syntax-und Ausnahmeeigenschaften.,

Typisierte Ausnahmen abfangen

Sie können mit den Ausnahmen, die Sie abfangen, selektiv sein. Ausnahmen haben einen Typ und Sie können spezifizierendie Art der Ausnahme, die Sie fangen möchten.

Der Ausnahmetyp wird für jeden catch – Block überprüft, bis einer gefunden wird, der mit Ihrem übereinstimmt exception.It es ist wichtig zu erkennen, dass Ausnahmen von anderen Ausnahmen erben können. Im obigen Beispiel erbtFileNotFoundException von IOException. Wenn also die IOException zuerst war, dann würde es stattdessen aufgerufen werden., Es wird nur ein Catch-Block aufgerufen, auch wenn mehrere Übereinstimmungen vorliegen.

Wenn wir eine System.IO.PathTooLongException hätten, würde die IOException übereinstimmen, aber wenn wir eineInsufficientMemoryException dann würde nichts es fangen und es würde den Stapel ausbreiten.

Fangen mehrere Typen auf einmal

Es ist möglich, mehrere catch-exception-Typen mit dem gleichen catch – Anweisung.

Vielen Dank /u/Sheppard_Ra, dass Sie diesen Zusatz vorgeschlagen haben.

Typisierte Ausnahmen auslösen

Sie können typisierte Ausnahmen in PowerShell auslösen., Anstatt throw mit einer Zeichenfolge aufzurufen:

throw "Could not find: $path"

Verwenden Sie einen Ausnahmebeschleuniger wie diesen:

throw "Could not find: $path"

Sie müssen jedoch eine Nachricht angeben, wenn Sie dies auf diese Weise tun.

Sie können auch eine neue Instanz einer Ausnahme erstellen, die ausgelöst werden soll. Die Nachricht ist optional, wenn Sie dies tun, da das System Standardmeldungen für alle integrierten Ausnahmen enthält.

Wenn Sie PowerShell 5.0 oder höher nicht verwenden, müssen Sie den älteren New-Object Ansatz verwenden.,

Wenn Sie eine typisierte Ausnahme verwenden, können Sie (oder andere) die Ausnahme nach dem Typ abfangen, wie im vorherigen Abschnitt erwähnt.

Write-Error-Exception

Wir können diese typisierten Ausnahmen zu Write-Error hinzufügen und wir können immer noch catch die Fehler nach exceptiontype. Verwenden Sie Write-Error wie in diesen Beispielen:

Dann können wir es so abfangen:

catch { Write-Log $PSItem.ToString()}

Die große Liste der. NET-Ausnahmen

Ich habe eine Master-Liste mit Hilfe der Reddit/r/PowerShell-Community zusammengestellt, die enthälthundreds von.,NET Ausnahmen zu ergänzen diesen Beitrag.

  • Die große Liste der. NET-Ausnahmen

Ich beginne damit, diese Liste nach Ausnahmen zu durchsuchen, die das Gefühl haben, dass sie gut zu mysituation passen. Sie sollten versuchen, Ausnahmen im Namespace base System zu verwenden.

Ausnahmen sind Objekte

Wenn Sie viele typisierte Ausnahmen verwenden, denken Sie daran, dass es sich um Objekte handelt. Verschiedene exceptionshave verschiedene Konstruktoren und Eigenschaften., Wenn wir uns die FileNotFoundExceptiondocumentation für System.IO.FileNotFoundException, sehen wir, dass wir eine Nachricht und einen Dateipfad übergeben können.

::new("Could not find file", $path)

Und es hat eine FileName Eigenschaft, die diesen Dateipfad verfügbar macht.

catch { Write-Output $PSItem.Exception.FileName}

Sie sollten die.NET-Dokumentation für andere Konstruktoren und Objekteigenschaften konsultieren.

Eine Ausnahme erneut auslösen

Wenn alles, was Sie in Ihrem catch – Block tun werden, throw ist die gleiche Ausnahme, dann nicht catches., Sie sollten nur catch eine Ausnahme, die Sie behandeln oder eine Aktion ausführen möchten, wenn es passiert.

Es gibt Zeiten, in denen Sie eine Aktion für eine Ausnahme ausführen möchten, aber die Ausnahme erneut auslösen möchten, damit Sie damit umgehen können. Wir könnten eine Nachricht schreiben oder das Problem in der Nähe der Stelle protokollieren, an der wir es entdecken, aber das Problem weiter oben im Stapel behandeln.

catch{ Write-Log $PSItem.ToString() throw $PSItem}

Interessanterweise können wir throw innerhalb der catch aufrufen und die currentexception erneut auslösen.,

catch{ Write-Log $PSItem.ToString() throw}

Wir möchten die Ausnahme erneut auslösen, um die ursprünglichen Ausführungsinformationen wie Quellskript und Zeilennummer beizubehalten. Wenn wir an dieser Stelle eine neue Ausnahme auslösen, wird ausgeblendet, wo die Ausnahme gestartet wurde.

Erneutes Auslösen einer neuen Ausnahme

Wenn Sie eine Ausnahme abfangen, aber eine andere auslösen möchten, sollten Sie die originalexception in die neue verschachteln. Auf diese Weise kann jemand auf dem Stapel als$PSItem.Exception.InnerExceptiondarauf zugreifen.

catch{ throw ::new('Could not access field',$PSItem.Exception)}

$PSCmdlet.,ThrowTerminatingError ()

Eine Sache, die ich an der Verwendung von throw für Raw-Ausnahmen nicht mag, ist, dass die Fehlermeldung bei der throw – Anweisung angezeigt wird und angibt, dass sich in dieser Zeile das Problem befindet.

Wenn mir die Fehlermeldung mitteilt, dass mein Skript defekt ist, weil ich throw in Zeile 31 aufgerufen habe, wird eine Fehlermeldung angezeigt, die Benutzer Ihres Skripts sehen können. Es sagt ihnen nichts Nützliches.

Dexter Dhami wies darauf hin, dass ich ThrowTerminatingError(), um das zu korrigieren.,

Wenn wir annehmen, dass ThrowTerminatingError() in einer Funktion namens Get-Resource aufgerufen wurde, dannDies ist der Fehler, den wir sehen würden.

Sehen Sie, wie es auf die Funktion Get-Resource als Ursache des Problems verweist? Das sagt dem Benutzer etwas Nützliches.

Da eine ErrorRecord, können wir auch ThrowTerminatingError auf diese Weise erneut werfen.,

catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}

Dadurch wird die Fehlerquelle in das Cmdlet geändert und die Interna Ihrer Funktion vor den Benutzern Ihres Cmdlet ausgeblendet.

Try can create terminating errors

Kirk Munro weist darauf hin, dass einige Ausnahmen nur Fehler beenden, wenn sie in einemtry/catch – Block ausgeführt werden. Hier ist das Beispiel, das er mir gegeben hat und das eine Laufzeitausnahme durch Null dividiert.

function Start-Something { 1/(1-1) }

Rufen Sie es dann so auf, um zu sehen, dass es den Fehler generiert und trotzdem die Nachricht ausgibt.,

&{ Start-Something; Write-Output "We did it. Send Email" }

Aber indem wir denselben Code in eine try/catch, sehen wir etwas anderes passieren.

try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}

Wir sehen, dass der Fehler zu einem Abschlussfehler wird und nicht die erste Nachricht ausgibt. Was ich an diesem nicht mag, ist, dass Sie diesen Code in einer Funktion haben können und es sich anders verhält, wenn jemand eine try/catch.

Ich bin selbst nicht auf Probleme gestoßen, aber es ist der Fall, auf den ich achten muss.

$PSCmdlet.,ThrowTerminatingError () in try/catch

Eine Nuance von $PSCmdlet.ThrowTerminatingError() ist, dass es einen Abschlussfehler in Ihrem Cmdlet erzeugt, aber es wird zu einem nicht abschließenden Fehler, nachdem es Ihr Cmdlet verlassen hat. Dadurch bleibt der burdenon der Aufrufer Ihrer Funktion, um zu entscheiden, wie mit dem Fehler umzugehen ist. Sie können es wieder in einen Auslöschungsfehler umwandeln, indem sie -ErrorAction Stop oder es aus einer try{...}catch{...}aufrufen.,

Öffentliche Funktionsvorlagen

Eine letzte Möglichkeit, die ich bei meinem Gespräch mit Kirk Munro hatte, war, dass er einetry{...}catch{...} um jede begin, process und end Block in all seinen advancedfunctions. In diesen generischen Catch-Blöcken hat er eine einzelne Zeile mit$PSCmdlet.ThrowTerminatingError($PSItem), um alle Ausnahmen zu behandeln, die seine Funktionen verlassen.

function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}

Da sich innerhalb seiner Funktionen alles in einer try – Anweisung befindet, verhält sich alles konsistent., Dies gibt dem Endbenutzer auch saubere Fehler, die den internen Code vor dem generierten Fehler verbergen.

Trap

Ich habe mich auf den try/catch Aspekt von Ausnahmen konzentriert. Aber es gibt eine Legacy-Funktion, die ich erwähnen muss, bevor wir das abschließen.

A trap wird in ein Skript oder eine Funktion eingefügt, um alle Ausnahmen abzufangen, die in diesem Bereich auftreten. Wenn eine Ausnahme auftritt, wird der Code in der trap ausgeführt und dann wird der normale Code fortgesetzt. Ifmultiple Ausnahmen passieren, dann wird die Falle immer und immer aufgerufen.,

Ich persönlich habe diesen Ansatz nie übernommen, aber ich kann den Wert in Admin-oder Controller-Skripten sehen, die alle Ausnahmen enthalten, und dann weiterhin ausführen.

Schlussbemerkungen

Das Hinzufügen einer ordnungsgemäßen Ausnahmebehandlung zu Ihren Skripten macht sie nicht nur stabiler, sondern macht es Ihnen auch einfacher, diese Ausnahmen zu beheben.

Ich habe viel Zeit damit verbracht, throw weil es ein Kernkonzept ist, wenn es um exceptionhandling geht., PowerShell gab uns auch Write-Error, das alle Situationen behandelt, in denen Siethrow. Denken Sie also nicht, dass Sie throw nachdem Sie dies gelesen haben.

Nachdem ich mir nun die Zeit genommen habe, in diesem Detail über die Ausnahmebehandlung zu schreiben, verwende ich Write-Error -Stop, um Fehler in meinem Code zu generieren. Ich werde auch den Rat von takeKirk befolgen und ThrowTerminatingError zu meinem goto-Ausnahmehandler für jede Funktion machen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.