wake-up-neo.com

Wann sollte ich Write-Error vs. Throw verwenden? Fehler beim Beenden oder Nichtbeenden

Als ich mir ein Get-WebFile-Skript auf PoshCode ansah, http://poshcode.org/3226 , bemerkte ich diese für mich seltsame Erfindung:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

Was ist der Grund dafür im Gegensatz zu den folgenden?

$URL_Format_Error = [string]"..."
Throw $URL_Format_Error

Oder noch besser:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

Soweit ich weiß, sollten Sie Write-Error verwenden, um Fehler nicht zu beenden, und Throw, um Fehler zu beenden. Aus diesem Grund sollte Write-Error gefolgt von Return nicht verwendet werden. Ist da ein Unterschied?

131
Bill Barry

Write-Error sollte verwendet werden, wenn Sie den Benutzer über einen nicht kritischen Fehler informieren möchten. Standardmäßig wird lediglich eine Fehlermeldung in roter Schrift auf der Konsole ausgegeben. Die Fortsetzung einer Pipeline oder einer Schleife wird nicht gestoppt. Throw hingegen erzeugt einen sogenannten Abbruchfehler. Wenn Sie throw verwenden, wird die Pipeline und/oder die aktuelle Schleife beendet. Tatsächlich wird jede Ausführung abgebrochen, es sei denn, Sie verwenden ein trap oder ein try/catch Struktur zur Behandlung des Abbruchfehlers.

Es gibt eine Sache zu beachten, wenn Sie $ErrorActionPreference bis "Stop" und benutze Write-Error es wird ein Abbruchfehler erzeugt.

In dem von Ihnen verlinkten Skript finden wir Folgendes:

if ($url.Contains("http")) {
       $request = [System.Net.HttpWebRequest]::Create($url)
}
else {
       $URL_Format_Error = [string]"Connection protocol not specified. Recommended action: Try again using protocol (for example 'http://" + $url + "') instead. Function aborting..."
       Write-Error $URL_Format_Error
    return
   }

Offenbar wollte der Autor dieser Funktion die Ausführung dieser Funktion stoppen und eine Fehlermeldung auf dem Bildschirm anzeigen, wollte jedoch nicht, dass das gesamte Skript nicht mehr ausgeführt wird. Der Autor des Skripts hätte throw verwenden können, dies würde jedoch bedeuten, dass Sie ein try/catch beim Aufruf der Funktion.

return beendet den aktuellen Bereich, der eine Funktion, ein Skript oder ein Skriptblock sein kann. Dies lässt sich am besten mit Code veranschaulichen:

# A foreach loop.
foreach ( $i in  (1..10) ) { Write-Host $i ; if ($i -eq 5) { return } }

# A for loop.
for ($i = 1; $i -le 10; $i++) { Write-Host $i ; if ($i -eq 5) { return } }

Ausgabe für beide:

1
2
3
4
5

Ein Punkt hier ist die Verwendung von return mit ForEach-Object. Die Verarbeitung wird nicht wie erwartet unterbrochen.

Mehr Informationen:

165
Andy Arismendi

Der Hauptunterschied zwischen dem Cmdlet Schreibfehler und dem Schlüsselwort throw in PowerShell besteht darin, dass das vorherige einfach gedruckt wird etwas Text zum Standardfehlerstrom (stderr) , während letzterer tatsächlich die Verarbeitung des laufenden Befehls beendet oder Funktion, die then von PowerShell verarbeitet wird, indem Informationen über den Fehler an die Konsole gesendet werden.

Sie können das unterschiedliche Verhalten der beiden in den Beispielen beobachten, die Sie bereitgestellt haben:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

In diesem Beispiel wurde das Schlüsselwort return hinzugefügt, um explizit die Ausführung des Skripts zu stoppen, nachdem die Fehlermeldung an die Konsole gesendet wurde. Im zweiten Beispiel hingegen ist das Schlüsselwort return nicht erforderlich, da die Beendigung implizit durch throw erfolgt:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
16

Wichtig : Es gibt 2 Arten von Abschlussfehlern , Was die aktuellen Hilfethemen leider verschmelzen :

  • Anweisung - Beenden von Fehlern, wie sie von Cmdlets in bestimmten nicht behebbaren Situationen und von Ausdrücken gemeldet werden, in denen eine .NET-Ausnahme/eine PS-Laufzeit auftritt Fehler tritt auf; nur die Anweisung wird beendet und die Skriptausführung wird standardmäßig fortgesetzt .

  • Skript - Beenden von Fehlern, die entweder durch Throw oder durch Eskalation eines der anderen Fehlertypen durch eine Fehleraktion ausgelöst wurden Präferenzvariable/Parameterwert Stop.
    Sofern sie nicht abgefangen werden, beenden sie den aktuellen Ausführungsthread (d. H. Nicht nur das aktuelle Skript, sondern gegebenenfalls auch alle seine Aufrufer).

Eine umfassende Übersicht über die Fehlerbehandlung in PowerShell finden Sie unter dieses GitHub-Dokumentationsproblem .

Der Rest dieses Beitrags konzentriert sich auf nicht-terminierende vs. Anweisungs-terminierende Fehler.


Ergänzen Sie die vorhandenen hilfreichen Antworten mit einem Fokus auf den Kern der Frage: Wie können Sie wählen ob Sie eine Aussage melden - beenden oder nicht beenden error ?

Cmdlet Error Reporting enthält hilfreiche Richtlinien; Lassen Sie mich eine pragmatische Zusammenfassung versuchen :

Die allgemeine Idee hinter nicht abschließenden Fehlern besteht darin, eine "fehlertolerante" Verarbeitung großer Eingabesätze zu ermöglichen : Fehler beim Verarbeiten einer Teilmenge von Die Eingabeobjekte sollten (standardmäßig) den - möglicherweise lang andauernden - Prozess nicht als Ganzes abbrechen, sodass Sie die Fehler überprüfen und nur die fehlgeschlagenen Objekte später erneut verarbeiten können - wie über die in der automatischen Variablen $Error gesammelten Fehlersätze gemeldet.

  • Melden Sie einen NICHT TERMINIERENDEN Fehler, wenn Ihr Cmdlet/Ihre erweiterte Funktion:

    • akzeptiert MULTIPLE-Eingabeobjekte über die Pipeline-Eingabe und/oder Array-Werte-Parameter und
    • Fehler treten für SPEZIFISCHE Eingabeobjekte UND auf
    • Diese Fehler VERHINDERN NICHT DIE VERARBEITUNG WEITERER Eingabeobjekte IM GRUNDSATZ ( situationsbedingt , möglicherweise sind keine Eingabeobjekte mehr vorhanden und/oder vorherige Eingabeobjekte wurde möglicherweise bereits erfolgreich verarbeitet).
      • Verwenden Sie in erweiterten Funktionen $PSCmdlet.WriteError(), um einen nicht beendenden Fehler zu melden (Write-Error Führt leider nicht dazu, dass $? In auf $False Gesetzt wird der Aufrufer Umfang - siehe dieses GitHub-Problem ).
      • Behandlung eines nicht beendenden Fehlers: $? Gibt an, ob der letzte Befehl mindestens einen nicht beendenden Fehler gemeldet hat.
        • Daher kann $? Als $False Bedeuten, dass eine beliebige (nicht leere) Teilmenge von Eingabeobjekten nicht ordnungsgemäß verarbeitet wurde, möglicherweise die gesamte Menge.
        • Die Präferenzvariable $ErrorActionPreference Und/oder der allgemeine Cmdlet-Parameter -ErrorAction Können das Verhalten von nicht beendenden Fehlern (nur) in Bezug auf das Fehlerausgabeverhalten ändern und festlegen, ob nicht beendende Fehler zu eskaliert werden sollen. script - terminating ones.
  • Melden Sie einen STATEMENT-TERMINATING Fehler in allen anderen Fällen .

    • Insbesondere , wenn ein Fehler in einem Cmdlet/einer erweiterten Funktion auftritt, die nur ein SINGLE- oder NO-Eingabeobjekt akzeptiert und NO- oder SINGLE-Ausgabeobjekt ausgibt, oder nur Parametereingabe und den Parameter verwendet Die angegebenen Werte verhindern einen sinnvollen Betrieb.
      • In erweiterten Funktionen müssen Sie $PSCmdlet.ThrowTerminatingError() verwenden, um einen anweisungsbeendenden Fehler zu generieren.
      • Beachten Sie, dass das Schlüsselwort Throw im Gegensatz dazu einen Skript - Abbruchfehler generiert, der das gesamte Skript (technisch: das aktuelle Thread ) abbricht.
      • Behandlung eines Anweisungsbeendigungsfehlers: Es kann ein try/catch - Handler oder eine trap - Anweisung verwendet werden (die nicht kann mit nichtbeendigenden Fehlern verwendet werden). Beachten Sie jedoch, dass selbst Anweisungsfehler - standardmäßig die Ausführung des restlichen Skripts nicht verhindern. Wie bei non-terminating errors gibt $?$False Wieder, wenn die vorherige Anweisung einen Fehler beim Beenden der Anweisung ausgelöst hat.

Leider halten sich nicht alle PowerShell-eigenen Core-Cmdlets an diese Regeln :

  • Obwohl dies unwahrscheinlich ist, würde New-TemporaryFile (PSv5 +) einen nicht beendenden Fehler melden, wenn er fehlschlägt, obwohl keine Pipelineeingabe akzeptiert und nur ein one output-Objekt erstellt wird. Dies wird sich jedoch wahrscheinlich in Version 6 ändern: siehe - diese GitHub Ausgabe .

  • Die Hilfe von Resume-Job Behauptet, dass die Übergabe eines nicht unterstützten Auftragstyps (z. B. eines mit Start-Job Erstellten Auftrags) nicht unterstützt wird, da Resume-Job Nur für workflow gilt. jobs) verursacht einen Abbruchfehler, der jedoch ab PSv5.1 nicht mehr zutrifft.

13
mklement0

Mit Write-Error Kann der Verbraucher der Funktion die Fehlermeldung mit -ErrorAction SilentlyContinue Unterdrücken (alternativ -ea 0). Während throw einen try{...} catch {..} Benötigt

Um einen Versuch zu machen ... fange mit Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}
7
Rynant

Ergänzung zu Andy Arismendis Antwort :

Ob Schreibfehler den Vorgang beendet oder nicht, hängt vom $ErrorActionPreference Einstellung.

Bei nicht trivialen Skripten wird $ErrorActionPreference = "Stop" ist ein empfohlene Einstellung , um schnell zu versagen.

"Das Standardverhalten von PowerShell in Bezug auf Fehler, das bei einem Fehler fortgesetzt werden soll, fühlt sich sehr VB6" On Error Resume Next "an."

(von http://codebetter.com/jameskovacs/2010/02/25/the-exec-problem/ )

Es macht jedoch Write-Error Anrufe werden beendet.

Um Schreibfehler als nicht beendenden Befehl unabhängig von anderen Umgebungseinstellungen zu verwenden, können Sie allgemeiner Parameter-ErrorAction mit Wert Continue:

 Write-Error "Error Message" -ErrorAction:Continue
5

Wenn Sie den Code richtig gelesen haben, sind Sie richtig. Zum Beenden von Fehlern sollte throw verwendet werden. Wenn Sie mit .NET-Typen arbeiten, ist es hilfreich, auch .NET-Ausnahmekonventionen zu befolgen.

0
yzorg