Quand devrais-je utiliser Write-Error vs Throw? Erreurs de terminaison vs erreurs non terminales

En regardant un script Get-WebFile sur PoshCode: http://poshcode.org/3226 J’ai remarqué ce truc étrange:

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

Quelle est la raison de ceci par opposition à:

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

ou même mieux:

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

Si je comprends bien, vous devez utiliser Write-Error pour les erreurs non terminantes et Throw pour les erreurs de terminaison. Il me semble donc que vous ne devez pas utiliser Write-Error suivi de Return. Y a-t-il une différence?

Write-Error doit être utilisé si vous souhaitez informer l’utilisateur d’une erreur non critique. Par défaut, tout ce qu’il fait est d’imprimer un message d’erreur en texte rouge sur la console. Cela n’empêche pas un pipeline ou une boucle de continuer. Throw par contre produit ce qu’on appelle une erreur de terminaison. Si vous utilisez jet, le pipeline et / ou la boucle de courant seront terminés. En fait, toute exécution sera terminée à moins que vous n’utilisiez une structure trap ou try/catch pour gérer l’erreur de fin.

Il y a une chose à noter, si vous définissez $ErrorActionPreference sur "Stop" et utilisez Write-Error cela produira une erreur de fin .

Dans le script que vous avez lié, nous trouvons ceci:

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

Il semble que l’auteur de cette fonction ait voulu arrêter l’exécution de cette fonction et afficher un message d’erreur à l’écran, mais ne voulait pas que tout le script cesse de s’exécuter. L’auteur du script aurait pu utiliser throw mais cela signifierait que vous devez utiliser un try/catch lors de l’appel de la fonction.

return quittera la scope actuelle qui peut être une fonction, un script ou un bloc de script. Ceci est mieux illustré avec le code:

 # 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 } } 

Sortie pour les deux:

 1 2 3 4 5 

Un getcha ici utilise return avec ForEach-Object . Cela ne cassera pas le traitement comme on pourrait s’y attendre.

Plus d’informations:

La principale différence entre l’applet de commande Write-Error et le mot-clé throw dans PowerShell est que le premier imprime simplement du texte dans le stream d’erreur standard (stderr) , tandis que le second termine le traitement de la commande ou de la fonction en cours d’exécution. par PowerShell en envoyant des informations sur l’erreur à la console.

Vous pouvez observer le comportement différent des deux dans les exemples que vous avez fournis:

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

Dans cet exemple, le mot clé return a été ajouté pour arrêter explicitement l’exécution du script après l’envoi du message d’erreur à la console. Dans le second exemple, en revanche, le mot-clé return n’est pas nécessaire car la terminaison est implicitement effectuée par throw :

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

Important : Il existe 2 types d’erreurs de terminaison , que les rubriques d’aide actuelles confondent malheureusement:

  • erreurs liées à la suppression d’ instruction , telles qu’elles sont signalées par les applets de commande dans certaines situations non récupérables et par les expressions dans lesquelles une erreur d’exécution / .NET PS se produit; seule l’ instruction est terminée et l’ exécution du script continue par défaut .

  • des erreurs de script- sortingggering , soit déclenchées par Throw soit en augmentant l’un des autres types d’erreur via la variable de préférence action-action / la valeur du paramètre Stop .
    À moins d’être attrapés, ils terminent le thread d’exécution en cours (c.-à-d. Pas seulement le script en cours, mais tous ses appelants, le cas échéant).

Pour une présentation complète de la gestion des erreurs de PowerShell, consultez ce problème de documentation de GitHub .

Le rest de cet article se concentre sur les erreurs non terminales par rapport aux erreurs de terminaison d’instruction .


Pour compléter les réponses utiles existantes en mettant l’accent sur l’essentiel de la question: comment choisissez- vous de signaler une erreur de fin ou de non-terminaison ?

Cmdlet Error Reporting contient des instructions utiles; laissez-moi tenter un résumé pragmatique :

L’idée générale derrière les erreurs non terminales est de permettre le traitement “tolérant aux pannes” de grands ensembles d’entrées : le traitement d’un sous-ensemble des objects d’entrée ne devrait pas (par défaut) interrompre le processus – potentiellement de longue durée – dans son ensemble, vous permettant d’inspecter les erreurs et de ne retraiter que les objects défaillants ultérieurement – comme indiqué dans les enregistrements d’erreur collectés dans la variable automatique $Error .

  • Signalez une erreur NON TERMINATOIRE si votre applet de commande / fonction avancée:

    • accepte des objects d’entrée MULTIPLES , via les parameters d’entrée et / ou de valeur de tableau du pipeline, ET
    • des erreurs se produisent pour les objects d’entrée SPECIFIC , ET
    • ces erreurs n’empêchent pas le traitement d’objects d’entrée supplémentaires dans PRINCIPE (dans la situation , il se peut qu’aucun object d’entrée ne soit laissé et / ou que les objects d’entrée précédents aient déjà été traités avec succès).
      • Dans les fonctions avancées, utilisez $PSCmdlet.WriteError() pour signaler une erreur sans fin ( Write-Error , malheureusement, ne définit pas $False dans la scope de l’ appelant – voir ce problème GitHub ).
      • Gérer une erreur sans fin: $? vous indique si la commande la plus récente a signalé au moins une erreur non terminée.
        • Donc, $? Être $False peut signifier que n’importe quel sousensemble (non vide) d’objects d’entrée n’a pas été correctement traité, peut-être l’ensemble entier.
        • Variable de préférence $ErrorActionPreference et / ou paramètre de cmdlet commun -ErrorAction peut modifier le comportement des erreurs non terminantes (uniquement) en termes de comportement de sortie d’erreur et si les erreurs non terminantes doivent être renvoyées à des erreurs de script .
  • Signaler une erreur STATEMENT-TERMINATING dans tous les autres cas .

    • Notamment, si une erreur survient dans une fonction d’applet de commande / avancée qui accepte uniquement un object d’entrée SINGLE ou NO et produit un object de sortie NO ou SINGLE.
      • Dans les fonctions avancées, vous devez utiliser $PSCmdlet.ThrowTerminatingError() pour générer une erreur de fin de déclaration.
      • Notez que le mot clé Throw génère en revanche une erreur d’interruption de script qui annule l’ intégralité du script .
      • Gestion d’ une erreur de fin de déclaration: une instruction try/catch handler ou trap peut être utilisée (qui ne peut pas être utilisée avec des erreurs non terminantes ), mais notez que même les erreurs de fin de commande par défaut n’empêchent pas l’exécution du rest du script . Comme avec les erreurs non terminales , $? reflète $False si l’instruction précédente a déclenché une erreur de fin d’instruction.

Malheureusement, toutes les cmdlets de base de PowerShell ne respectent pas ces règles :

  • Bien qu’improbable, New-TemporaryFile (PSv5 +) signalerait une erreur non résiliante en cas d’échec, même s’il n’acceptait pas l’entrée de pipeline et ne produisait qu’un seul object de sortie – cela changerait probablement en v6: voir ce problème GitHub .

  • L’aide de Resume-Job indique que le fait de transmettre un type de travail non pris en charge (par exemple, un travail créé avec Start-Job , qui n’est pas pris en charge car Resume-Job s’applique uniquement aux travaux de workflow ) entraîne une erreur de terminaison. PSv5.1.

Write-Error permet au consommateur de la fonction de supprimer le message d’erreur avec -ErrorAction SilentlyContinue (alternativement -ea 0 ). Bien que throw nécessite un try{...} catch {..}

Pour utiliser un try … catch with Write-Error :

 try { SomeFunction -ErrorAction Stop } catch { DoSomething } 

Ajout à la réponse d’Andy Arismendi :

Le fait que Write-Error termine ou non le processus dépend du paramètre $ErrorActionPreference .

Pour les scripts non sortingviaux, $ErrorActionPreference = "Stop" est un paramètre recommandé pour échouer rapidement.

“Le comportement par défaut de PowerShell vis-à-vis des erreurs, qui doit continuer en cas d’erreur … se sent très VB6” On Error Resume Next “-ish”

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

Cependant, les appels en Write-Error terminent.

Pour utiliser Write-Error en tant que commande sans terminaison indépendamment des autres parameters d’environnement, vous pouvez utiliser le paramètre commun -ErrorAction avec la valeur Continue :

  Write-Error "Error Message" -ErrorAction:Continue 

Si votre lecture du code est correcte, vous avez raison. Les erreurs de terminaison doivent utiliser throw , et si vous utilisez des types .NET, il est utile de suivre également les conventions d’exception .NET.