Alles, was Sie schon immer über Ausnahmen wissen wollten
Die Fehlerbehandlung gehört beim Schreiben von Code einfach dazu. Wir können häufig die Bedingungen für erwartetes Verhalten überprüfen und validieren. Bei unerwartetem Verhalten kommt die Ausnahmebehandlung ins Spiel. Sie können ganz einfach Ausnahmen behandeln, die durch den Code anderer Programmierer generiert werden, oder Sie können selbst Ausnahmen generieren, die andere behandeln können.
Hinweis
Die Originalversion dieses Artikels erschien ursprünglich in einem Blog von @KevinMarquette. Das PowerShell-Team dankt Kevin Marquette, dass er diesen Inhalt mit uns teilt. Weitere Informationen finden Sie in seinem Blog auf PowerShellExplained.com.
Grundlegende Terminologie
Wir müssen einige grundlegende Begriffe klären, bevor wir uns mit diesem Thema befassen.
Ausnahme
Eine Ausnahme ist wie ein Ereignis, das ausgelöst wird, wenn die normale Fehlerbehandlung das Problem nicht beheben kann. Der Versuch, eine Zahl durch 0 zu teilen, oder unzureichender Arbeitsspeicher sind Beispiele für Ereignisse, die eine Ausnahme erzeugen. Manchmal erstellt der Autor des Codes, den Sie verwenden, Ausnahmen für bestimmte Probleme, falls diese auftreten.
Throw und Catch
Wenn eine Ausnahme auftritt, wird dies als „Auslösen einer Ausnahme“ bezeichnet. Um eine ausgelöste Ausnahme zu behandeln, müssen Sie sie abfangen. Wenn eine Ausnahme ausgelöst und nicht abgefangen wird, wird die Ausführung des Skripts beendet.
Die Aufrufliste
Die Aufrufliste ist die Liste der Funktionen, die sich gegenseitig aufgerufen haben. Wenn eine Funktion aufgerufen wird, wird sie am Anfang der Liste hinzugefügt. Eine Funktion, die beendet oder zurückgegeben wird, wird aus der Liste entfernt.
Wenn eine Ausnahme ausgelöst wird, wird diese Aufrufliste eingecheckt, damit sie von einem Ausnahmehandler abgefangen werden kann.
Fehler mit und ohne Abbruch
Eine Ausnahme ist im Allgemeinen ein Fehler mit Abbruch. Eine ausgelöste Ausnahme wird entweder abgefangen oder beendet die aktuelle Ausführung. Standardmäßig wird ein Fehler ohne Abbruch durch Write-Error
generiert, und dem Ausgabestream wird ein Fehler hinzugefügt, ohne dass eine Ausnahme ausgelöst wird.
Dies sollten Sie unbedingt beachten, da Write-Error
und andere Fehler ohne Abbruch catch
nicht auslösen.
Behalten einer Ausnahme
Dies ist das Verhalten, wenn Sie einen Fehler abfangen, um ihn zu unterdrücken. Gehen Sie dabei sehr vorsichtig vor, da dies die Problembehandlung sehr schwierig machen kann.
Grundlegende Befehlssyntax
Im Folgenden finden Sie eine kurze Übersicht über die in PowerShell verwendete grundlegende Syntax für die Ausnahmebehandlung.
Throw
Zum Erstellen unseres eigenen Ausnahmeereignisses lösen wir mit dem Schlüsselwort throw
eine Ausnahme aus.
function Start-Something
{
throw "Bad thing happened"
}
Dadurch wird eine Laufzeitausnahme erstellt, die ein Fehler mit Abbruch ist. Sie wird von einem catch
in einer Aufruffunktion verarbeitet oder beendet das Skript mit einer Meldung wie der folgenden.
PS> Start-Something
Bad thing happened
At line:1 char:1
+ throw "Bad thing happened"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Bad thing happened:String) [], RuntimeException
+ FullyQualifiedErrorId : Bad thing happened
Write-Error: -ErrorAction Stop
Ich habe bereits erwähnt, dass Write-Error
nicht standardmäßig einen Fehler mit Abbruch auslöst. Wenn Sie -ErrorAction Stop
angeben, generiert Write-Error
einen Fehler mit Abbruch, der mit einem catch
behandelt werden kann.
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
Vielen Dank an Lee Dailey, der daran erinnert hat, dass -ErrorAction Stop
sich auch auf diese Weise verwenden lässt.
Cmdlet: -ErrorAction Stop
Wenn Sie -ErrorAction Stop
in einer beliebigen erweiterten Funktion oder einem Cmdlet angeben, werden alle Write-Error
-Anweisungen in Fehler mit Abbruch umgewandelt, die die Ausführung anhalten oder von einem catch
verarbeitet werden können.
Start-Something -ErrorAction Stop
Weitere Informationen zum Parameter ErrorAction finden Sie unter about_CommonParameters. Weitere Informationen zur Variable $ErrorActionPreference
finden Sie unter about_Preference_Variables.
Try/Catch
Die Ausnahmebehandlung in PowerShell (und vielen anderen Sprachen) funktioniert so, dass Sie zuerst einen try
-Vorgang für einen Codeabschnitt ausführen. Wenn dadurch ein Fehler ausgelöst wird, können Sie diesen mit catch
abfangen. Im Folgenden sehen Sie ein kurzes Beispiel.
try
{
Start-Something
}
catch
{
Write-Output "Something threw an exception"
Write-Output $_
}
try
{
Start-Something -ErrorAction Stop
}
catch
{
Write-Output "Something threw an exception or used Write-Error"
Write-Output $_
}
Das catch
-Skript wird nur ausgeführt, wenn ein Fehler mit Abbruch vorliegt. Bei korrekter Ausführung von try
wird catch
übersprungen. Sie können mithilfe der Variable $_
auf die Ausnahmeinformationen im Block catch
zugreifen.
Try/Finally
Es gibt Fälle, in denen Sie einen Fehler nicht behandeln müssen, aber dennoch Code benötigen, der ausgeführt wird, wenn eine Ausnahme auftritt oder nicht. Genau das macht ein finally
-Skript.
Im Folgenden finden Sie ein Beispiel:
$command = [System.Data.SqlClient.SqlCommand]::New(queryString, connection)
$command.Connection.Open()
$command.ExecuteNonQuery()
$command.Connection.Close()
Jedes Mal, wenn Sie eine Verbindung mit einer Ressource herstellen oder öffnen, sollten Sie die Verbindung auch wieder schließen. Wenn ExecuteNonQuery()
eine Ausnahme auslöst, wird die Verbindung nicht geschlossen. Hier ist derselbe Code in einem try/finally
-Block.
$command = [System.Data.SqlClient.SqlCommand]::New(queryString, connection)
try
{
$command.Connection.Open()
$command.ExecuteNonQuery()
}
finally
{
$command.Connection.Close()
}
In diesem Beispiel wird die Verbindung geschlossen, wenn ein Fehler vorliegt. Sie wird auch geschlossen, wenn kein Fehler vorliegt. Das finally
-Skript wird jedes Mal ausgeführt.
Da die Ausnahme nicht abgefangen wird, wird sie weiterhin in der Aufrufliste nach oben weitergegeben.
Try/Catch/Finally
Es ist völlig zulässig, catch
und finally
in Kombination zu verwenden. In den meisten Fällen werden Sie sicher nur eine der Optionen benötigen, aber es kann durchaus Szenarien geben, in denen Sie beide verwenden.
$PSItem
So, die Grundlagen sind bekannt – jetzt können wir etwas genauer ins Detail gehen.
Innerhalb des catch
-Blocks gibt es eine automatische Variable ($PSItem
oder $_
) vom Typ ErrorRecord
, die die Details zur Ausnahme enthält. Im Folgenden sehen Sie eine kurze Übersicht über einige der wichtigsten Eigenschaften.
In diesen Beispielen habe ich in ReadAllText
einen ungültigen Pfad verwendet, um diese Ausnahme zu generieren.
[System.IO.File]::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString()
Damit erhalten Sie eine klare und eindeutige Meldung, die Sie bei der Protokollierung und in Ausgaben ganz allgemein verwenden können. ToString()
wird automatisch aufgerufen, wenn $PSItem
in eine Zeichenfolge eingefügt wird.
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 von PowerShell gesammelte Informationen über die Funktion oder das Skript, in der bzw. dem die Ausnahme ausgelöst wurde. Hier sehen Sie die InvocationInfo
aus der Beispielausnahme, die ich erstellt habe.
PS> $PSItem.InvocationInfo | Format-List *
MyCommand : Get-Resource
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 5
OffsetInLine : 5
ScriptName : C:\blog\throwerror.ps1
Line : Get-Resource
PositionMessage : At C:\blog\throwerror.ps1:5 char:5
+ Get-Resource
+ ~~~~~~~~~~~~
PSScriptRoot : C:\blog
PSCommandPath : C:\blog\throwerror.ps1
InvocationName : Get-Resource
Die wichtigen Details zeigen hier den ScriptName
, die Line
des Codes und die ScriptLineNumber
, wo der Aufruf begann.
$PSItem.ScriptStackTrace
Diese Eigenschaft zeigt die Reihenfolge der Funktionsaufrufe, über die Sie zu dem Code gelangen, in dem die Ausnahme generiert wurde.
PS> $PSItem.ScriptStackTrace
at Get-Resource, C:\blog\throwerror.ps1: line 13
at Start-Something, C:\blog\throwerror.ps1: line 5
at <ScriptBlock>, C:\blog\throwerror.ps1: line 18
Ich führe nur Aufrufe von Funktionen im selben Skript durch, aber hiermit würden die Aufrufe nachverfolgt, wenn mehrere Skripts beteiligt wären.
$PSItem.Exception
Dies ist die eigentliche Ausnahme, die ausgelöst wurde.
$PSItem.Exception.Message
Hier sehen Sie die allgemeine Meldung, in der die Ausnahme beschrieben wird. Diese ist ein guter Ausgangspunkt für die Problembehandlung. Für die meisten Ausnahmen gibt es eine Standardmeldung, Sie können aber auch benutzerdefinierte Meldungen festlegen, die beim Auslösen einer Ausnahme angezeigt werden.
PS> $PSItem.Exception.Message
Exception calling "ReadAllText" with "1" argument(s): "The network path was not found."
Dies ist auch die Meldung, die beim Aufrufen von $PSItem.ToString()
zurückgegeben wird, wenn nichts im ErrorRecord
festgelegt wurde.
$PSItem.Exception.InnerException
Ausnahmen können innere Ausnahmen enthalten. Dies ist häufig der Fall, wenn der aufgerufene Code eine Ausnahme abfängt und eine andere Ausnahme auslöst. Die ursprüngliche Ausnahme wird in die neue Ausnahme eingefügt.
PS> $PSItem.Exception.InnerExceptionMessage
The network path was not found.
Ich werde dies später genauer erläutern, wenn es um das erneute Auslösen von Ausnahmen geht.
$PSItem.Exception.StackTrace
Das ist die StackTrace
(Stapelüberwachung) für die Ausnahme. Ich habe oben eine ScriptStackTrace
gezeigt, aber diese hier ist für die Aufrufe von verwaltetem Code vorgesehen.
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 diese Stapelüberwachung nur, wenn das Ereignis durch verwalteten Code ausgelöst wird. Ich rufe direkt eine .NET Framework-Funktion auf. Das ist also alles, was wir in diesem Beispiel sehen können. Wenn Sie sich eine Stapelüberwachung ansehen, sollten Sie nach der Stelle suchen, an der Ihr Code aufhört und die Systemaufrufe beginnen.
Arbeiten mit Ausnahmen
Das Thema Ausnahmen umfasst weit mehr als die grundlegende Syntax und Ausnahmeeigenschaften.
Abfangen typisierter Ausnahmen
Sie können auswählen, welche Ausnahmen Sie abfangen wollen. Ausnahmen besitzen einen Typ, und Sie können angeben, welchen Ausnahmetyp Sie abfangen möchten.
try
{
Start-Something -Path $path
}
catch [System.IO.FileNotFoundException]
{
Write-Output "Could not find $path"
}
catch [System.IO.IOException]
{
Write-Output "IO error with the file: $path"
}
Der Ausnahmetyp wird für jeden catch
-Block geprüft, bis ein Typ gefunden wird, der mit der Ausnahme übereinstimmt.
Sie müssen wissen, dass Ausnahmen von anderen Ausnahmen erben können. Im Beispiel oben erbt FileNotFoundException
von IOException
. Wenn also zuerst IOException
aufgetreten ist, würde diese Ausnahme stattdessen verarbeitet. Auch bei mehreren Übereinstimmungen wird nur ein catch-Block aufgerufen.
Wenn wir beispielsweise eine System.IO.PathTooLongException
hätten, würde die IOException
übereinstimmen, aber wenn wir aber eine InsufficientMemoryException
hätten, dann würde diese nicht abgefangen und stattdessen im Stapel nach oben weitergegeben.
Gleichzeitiges Abfangen mehrerer Typen
Es ist möglich, mehrere Ausnahmetypen mit der gleichen catch
-Anweisung abzufangen.
try
{
Start-Something -Path $path -ErrorAction Stop
}
catch [System.IO.DirectoryNotFoundException],[System.IO.FileNotFoundException]
{
Write-Output "The path or file was not found: [$path]"
}
catch [System.IO.IOException]
{
Write-Output "IO error with the file: [$path]"
}
Vielen Dank an Redditor u/Sheppard_Ra
für diesen Vorschlag.
Auslösen typisierter Ausnahmen
Sie können in PowerShell typisierte Ausnahmen auslösen. Anstatt wie folgt throw
mit einer Zeichenfolge aufzurufen:
throw "Could not find: $path"
Verwenden Sie stattdessen einen Ausnahmebeschleuniger wie den folgenden:
throw [System.IO.FileNotFoundException] "Could not find: $path"
In diesem Fall müssen Sie jedoch eine Meldung angeben.
Sie können auch eine neue Instanz einer Ausnahme erstellen, die ausgelöst werden soll. In diesem Fall ist die Meldung optional, da das System über Standardmeldungen für alle integrierten Ausnahmen verfügt.
throw [System.IO.FileNotFoundException]::new()
throw [System.IO.FileNotFoundException]::new("Could not find path: $path")
Wenn Sie nicht PowerShell 5.0 oder höher verwenden, müssen Sie den älteren Ansatz New-Object
verwenden.
throw (New-Object -TypeName System.IO.FileNotFoundException )
throw (New-Object -TypeName System.IO.FileNotFoundException -ArgumentList "Could not find path: $path")
Indem Sie eine typisierte Ausnahme verwenden, können Sie (oder andere) die Ausnahme nach Typ abfangen, wie im vorherigen Abschnitt erläutert.
Write-Error: -Exception
Wir können diese typisierten Ausnahmen zu Write-Error
hinzufügen und weiterhin einen catch
-Vorgang für die Fehler nach Ausnahmetyp durchführen. Verwenden Sie Write-Error
wie in diesen Beispielen:
# with normal message
Write-Error -Message "Could not find path: $path" -Exception ([System.IO.FileNotFoundException]::new()) -ErrorAction Stop
# With message inside new exception
Write-Error -Exception ([System.IO.FileNotFoundException]::new("Could not find path: $path")) -ErrorAction Stop
# Pre PS 5.0
Write-Error -Exception ([System.IO.FileNotFoundException]"Could not find path: $path") -ErrorAction Stop
Write-Error -Message "Could not find path: $path" -Exception (New-Object -TypeName System.IO.FileNotFoundException) -ErrorAction Stop
Dann können wir sie wie folgt abfangen:
catch [System.IO.FileNotFoundException]
{
Write-Log $PSItem.ToString()
}
Die lange Liste der .NET-Ausnahmen
Ich habe mithilfe der Reddit r/PowerShell
Community eine Masterliste mit Hunderten .NET-Ausnahmen als Ergänzung für diesen Beitrag zusammengestellt.
Ich durchsuche diese Liste zunächst nach Ausnahmen, die meiner Ansicht nach gut zu meiner Situation passen. Versuchen Sie, Ausnahmen im Basis-System
-Namespace zu verwenden.
Ausnahmen sind Objekte
Wenn Sie damit anfangen, viele typisierte Ausnahmen zu verwenden, behalten Sie im Hinterkopf, dass es sich um-Objekte handelt. Unterschiedliche Ausnahmen besitzen unterschiedliche Konstruktoren und Eigenschaften. Wenn Sie in der FileNotFoundException-Dokumentation nach System.IO.FileNotFoundException
suchen, sehen Sie, dass Sie eine Meldung und einen Dateipfad übergeben können.
[System.IO.FileNotFoundException]::new("Could not find file", $path)
Und es gibt eine FileName
-Eigenschaft, die diesen Dateipfad verfügbar macht.
catch [System.IO.FileNotFoundException]
{
Write-Output $PSItem.Exception.FileName
}
Weitere Konstruktoren und Objekteigenschaften finden Sie in der .NET-Dokumentation.
Erneutes Auslösen einer Ausnahme
Wenn Sie in Ihrem catch
-Block lediglich einen throw
-Vorgang für dieselbe Ausnahme durchführen möchten, dann führen Sie keinen catch
-Vorgang durch. Sie sollten nur einen catch
-Vorgang für eine Ausnahme durchführen, die Sie behandeln oder für die Sie eine Aktion ausführen möchten, wenn sie auftritt.
Es gibt Fälle, in denen Sie eine Aktion für eine Ausnahme ausführen, die Ausnahme aber erneut auslösen möchten, damit ein nachgeschalteter Prozess sie verarbeiten kann. Wir könnten eine Meldung schreiben oder das Problem in der Nähe der Stelle protokollieren, an der wir es entdeckt haben, das Problem aber weiter oben im Stapel verarbeiten.
catch
{
Write-Log $PSItem.ToString()
throw $PSItem
}
Interessanterweise können wir throw
innerhalb von catch
aufrufen, und die aktuelle Ausnahme wird erneut ausgelöst.
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 verborgen, wo die Ausnahme begonnen hat.
Erneutes Auslösen einer neuen Ausnahme
Wenn Sie eine Ausnahme abfangen, aber eine andere auslösen möchten, sollten Sie die ursprüngliche Ausnahme in der neuen Ausnahme schachteln. Dadurch kann weiter unten im Stapel als $PSItem.Exception.InnerException
darauf zugegriffen werden.
catch
{
throw [System.MissingFieldException]::new('Could not access field',$PSItem.Exception)
}
$PSCmdlet.ThrowTerminatingError()
Das Einzige, was mir an der Verwendung von throw
für unformatierte Ausnahmen nicht gefällt, ist die Tatsache, dass die Fehlermeldung auf die throw
-Anweisung zeigt und angibt, dass sich das Problem in dieser Zeile befindet.
Unable to find the specified file.
At line:31 char:9
+ throw [System.IO.FileNotFoundException]::new()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], FileNotFoundException
+ FullyQualifiedErrorId : Unable to find the specified file.
Eine Meldung zu einem Fehler im Skript aufgrund des Aufrufs von throw
in Zeile 31 ist für die Benutzer Ihres Skripts nicht wirklich brauchbar. Sie erhalten dadurch keine hilfreichen Informationen.
Dexter Dhami hat darauf hingewiesen, dass ich ThrowTerminatingError()
verwenden kann, um dies zu korrigieren.
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
([System.IO.FileNotFoundException]"Could not find $Path"),
'My.ID',
[System.Management.Automation.ErrorCategory]::OpenError,
$MyObject
)
)
Wenn wir davon ausgehen, dass ThrowTerminatingError()
in einer Funktion namens Get-Resource
aufgerufen wurde, sollten wir den folgenden Fehler sehen.
Get-Resource : Could not find C:\Program Files (x86)\Reference
Assemblies\Microsoft\Framework\.NETPortable\v4.6\System.IO.xml
At line:6 char:5
+ Get-Resource -Path $Path
+ ~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Get-Resource], FileNotFoundException
+ FullyQualifiedErrorId : My.ID,Get-Resource
Sehen Sie, wie auf die Get-Resource
-Funktion als Quelle des Problems verwiesen wird? Das sind hilfreiche Informationen für Benutzer.
Da $PSItem
ein ErrorRecord
ist, können wir auch ThrowTerminatingError
auf diese Weise zum erneuten Auslösen verwenden.
catch
{
$PSCmdlet.ThrowTerminatingError($PSItem)
}
Dadurch wird die Fehlerquelle für das Cmdlet geändert, und die Interna Ihrer Funktion werden vor den Benutzern Ihres Cmdlets verborgen.
„try“ kann Fehler mit Abbruch erstellen
Kirk Munro weist darauf hin, dass einige Ausnahmen nur bei Ausführung in einem try/catch
-Block Fehler mit Abbruch sind. Er hat mir das folgende Beispiel zur Verfügung gestellt, das eine Laufzeitausnahme aufgrund einer Division durch Null generiert.
function Start-Something { 1/(1-1) }
Führen Sie den Aufruf wie folgt aus. Sie werden sehen, dass der Fehler generiert und die Meldung trotzdem ausgegeben wird.
&{ Start-Something; Write-Output "We did it. Send Email" }
Wenn Sie jedoch denselben Code in einem try/catch
-Block einfügen, passiert etwas anderes.
try
{
&{ Start-Something; Write-Output "We did it. Send Email" }
}
catch
{
Write-Output "Notify Admin to fix error and send email"
}
Der Fehler wird zu einem Fehler mit Abbruch, und die erste Meldung wird nicht ausgegeben. Ein Aspekt gefällt mir hier allerdings nicht: Sie können diesen Code in einer Funktion einsetzen, und er verhält sich anders, wenn jemand try/catch
verwendet.
Ich selbst hatte damit keine Probleme, aber es ist ein – wenngleich seltener – Fall, den Sie kennen müssen.
$PSCmdlet.ThrowTerminatingError() in „try/catch“
Eine Besonderheit von $PSCmdlet.ThrowTerminatingError()
besteht darin, dass ein Fehler mit Abbruch innerhalb Ihres Cmdlets erstellt wird, der jedoch nach Verlassen des Cmdlets in einen Fehler ohne Abbruch umgewandelt wird. Dadurch muss der Aufrufer Ihrer Funktion entscheiden, wie der Fehler behandelt werden soll. Der Fehler kann mit -ErrorAction Stop
wieder in einen Fehler mit Abbruch umgewandelt oder in try{...}catch{...}
aufgerufen werden.
Öffentliche Funktionsvorlagen
Eine letzte Information, die ich aus meinem Gespräch mit Kirk Munro mitgenommen habe: Er platziert einen try{...}catch{...}
-Block um jeden begin
-, process
- und end
-Block in all seinen erweiterten Funktionen. In diesen allgemeinen Catch-Blöcken verwendet er eine einzige Zeile mit $PSCmdlet.ThrowTerminatingError($PSItem)
, um alle Ausnahmen zu behandeln, die seine Funktionen verlassen.
function Start-Something
{
[CmdletBinding()]
param()
process
{
try
{
...
}
catch
{
$PSCmdlet.ThrowTerminatingError($PSItem)
}
}
}
Da sich alles in einer try
-Anweisung innerhalb seiner Funktionen befindet, ist die gesamte Verarbeitung konsistent. Dadurch erhält der Endbenutzer auch eindeutige Fehler, die den internen Code aus dem generierten Fehler ausblenden.
Trap
Ich habe mich hier auf den try/catch
-Aspekt von Ausnahmen konzentriert. Es gibt jedoch ein älteres Feature, das ich erwähnen muss, bevor wir zum Ende kommen.
Ein trap
wird in ein Skript oder eine Funktion platziert, um alle Ausnahmen abzufangen, die in diesem Gültigkeitsbereich auftreten. Wenn eine Ausnahme auftritt, wird der Code im trap
ausgeführt, und anschließend wird der normale Code fortgesetzt. Wenn mehrere Ausnahmen auftreten, wird das Trap immer wieder aufgerufen.
trap
{
Write-Log $PSItem.ToString()
}
throw [System.Exception]::new('first')
throw [System.Exception]::new('second')
throw [System.Exception]::new('third')
Ich persönlich habe diesen Ansatz nie verwendet, kann aber den Nutzen in Admin- oder Controllerskripts sehen, die jede einzelne Ausnahme protokollieren und dann trotzdem weiter ausgeführt werden.
Abschließende Hinweise
Mit einer vernünftigen Ausnahmebehandlung werden Ihre Skripts nicht nur stabiler, sondern Sie können Probleme mit diesen Ausnahmen auch einfacher beheben.
Ich habe throw
sehr ausführlich erläutert, weil es eins der Kernkonzepte der Ausnahmebehandlung ist. PowerShell bietet auch Write-Error
zum Verarbeiten aller Situationen, in denen Sie throw
verwenden würden. Denken Sie also nach der Lektüre dieses Artikels nicht, dass Sie throw
unbedingt verwenden müssen.
Nachdem ich die Ausnahmebehandlung so ausführlich beschrieben habe, werde ich jetzt Write-Error -Stop
verwenden, um Fehler in meinem Code zu generieren. Ich werde auch den Rat von Kirk beherzigen und ThrowTerminatingError
als bevorzugten Ausnahmehandler für jede Funktion verwenden.