Quand dois-je utiliser Write-Error vs Throw? Erreurs terminales et non terminales

Regarder un script Get-WebFile sur PoshCode: http://poshcode.org/3226 J'ai remarqué cet étrange engin:

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

Quelle est la raison de ceci par opposition à:

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

ou même mieux:

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

si je comprends bien, vous devriez utiliser Write-Error pour les erreurs non terminantes, et Throw pour les erreurs terminantes, donc il me semble que vous ne devriez pas utiliser Ecrire-erreur suivie d'un retour. Est-il une différence?

112
demandé sur mklement0 2012-02-15 18:13:36

6 réponses

Write-Error doit être utilisé si vous souhaitez informer l'utilisateur d'une erreur non critique. Par défaut, il suffit 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 produit par contre ce qu'on appelle une erreur terminante. Si vous utilisez throw, le pipeline et/ou la boucle de courant sera terminé. En fait, toute exécution sera terminée à moins que vous n'utilisiez une structure trap ou try/catch pour gérer la résiliation de l'erreur.

il y a une chose à noter, Si vous mettez $ErrorActionPreference à "Stop" et utilisez Write-Error il va produire une erreur finale .

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

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
}

Il semble que l'auteur de cette fonction voulais arrêter l'exécution de cette fonction et afficher un message d'erreur à l'écran, mais ne voulait pas le script entier pour arrêter l'exécution. L'auteur du script aurait pu utiliser throw mais cela voudrait dire que vous auriez à utiliser un try/catch lors de l'appel de la fonction.

return sort de la portée actuelle qui peut être un bloc de fonctions, de scripts ou de scripts. Ceci est mieux illustré par 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

Une chasse aux sorcières ici est d'utiliser return avec ForEach-Object . Il ne cassera pas le traitement comme on pourrait s'y attendre.

plus d'informations:

145
répondu Andy Arismendi 2018-08-30 06:56:00

la principale différence entre le Write-Error cmdlet et le throw mot-clé dans PowerShell est que le premier tout simplement imprime un peu de texte à la flux d'erreur standard (stderr) , alors que le dernier en fait se termine le traitement de la commande en cours d'exécution ou fonction, qui est puis manipulé par PowerShell en envoyant des informations A propos de l'erreur sur la console.

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

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

dans cet exemple, le mot-clé return a été ajouté à explicitement arrêter l'exécution du script après que le message d'erreur a été envoyé à la console. Dans le second exemple, par contre, le mot-clé return n'est pas nécessaire puisque la terminaison est implicitement faite par throw :

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
14
répondu Enrico Campidoglio 2012-02-15 14:47:08

Important : il y a 2 types d'erreurs de terminaison , que les sujets d'aide actuels malheureusement confondre :

  • énoncé - fin des erreurs , tel que rapporté par les cmdlets dans certaines situations non récupérables et par des expressions dans lesquelles A. Une exception net / un exécution PS l'erreur se produit; seule l'instruction est terminée, et l'exécution du script continue par défaut .

  • script - suppression des erreurs , déclenchées soit par Throw , soit par l'escalade de l'un des autres types d'erreurs via l'action error-preference-variable / parameter value Stop .

    À moins d'être pris, ils se terminent le fil courant de l'exécution (c'est-à-dire pas seulement le script courant, mais aussi tous ses appelants, le cas échéant).

pour un aperçu complet du traitement des erreurs de PowerShell, voir ce numéro de documentation GitHub .

le reste de ce post se concentre sur non-terminante vs. déclaration-terminante erreurs.


pour compléter les réponses utiles existantes en mettant l'accent sur le cœur de la question: Comment faire choisir si vous devez déclarer une déclaration - se terminant ou ne se terminant pas erreur ?

Applet de commande de rapport d'Erreur contient les lignes directrices utiles; permettez-moi de tenter une 1519230920" pragmatique "résumé de la 1519240920" :

l'idée générale derrière erreurs non-terminantes est de permettre le traitement "tolérant les défauts" de grands ensembles d'entrée : échec de traiter un sous-ensemble des objets d'entrée ne devrait pas (par défaut) avorter le - potentiellement long-processus en tant qu'ensemble, vous permettant d'inspecter les erreurs et ne traite que le échoué objets plus tard - tel que rapporté par les enregistrements d'erreur recueillis dans variable automatique $Error .

  • Rapport NON-TERMINAISON erreur, si votre applet de commande / fonction avancée:

    • accepte plusieurs objets d'entrée , via les paramètres d'entrée et/ou de valeur du réseau de pipeline, et
    • des erreurs se produisent pour des objets d'entrée spécifiques , et
    • ces les erreurs N'empêchent pas le traitement D'autres objets d'entrée en principe ( situationnellement , il peut ne rester aucun objet d'entrée et/ou des objets d'entrée antérieurs peuvent déjà avoir été traités avec succès).
      • dans les fonctions avancées, utilisez $PSCmdlet.WriteError() pour signaler une erreur non-terminante ( Write-Error , malheureusement, ne fait pas que $? soit défini à $False dans le caller's portée - voir cette GitHub question ).
      • Manipulation un non-terminaison d'erreur: $? vous indique si la commande la plus récente a rapporté au moins un non-terminaison d'erreur.
        • ainsi, $? étant $False peut signifier que tout (non vide) sous-ensemble d'objets d'entrée n'a pas été correctement traité, peut-être l'ensemble.
        • la variable de préférence $ErrorActionPreference et / ou le paramètre cmdlet commun -ErrorAction peut modifier le comportement des erreurs non terminantes (seulement) en termes de comportement de sortie d'erreur et si les erreurs non terminantes doivent être escaladées à script -terminaisons ceux.
  • Report a STATEMENT-TERMINING error in tous les autres cas .

    • notamment, si une erreur se produit dans une fonction cmdlet / advanced qui n'accepte qu'un seul ou aucun objet d'entrée et ne produit aucun ou un seul objet de sortie.
      • dans les fonctions avancées, vous devez utiliser $PSCmdlet.ThrowTerminatingError() pour générer une erreur de terminaison de déclaration.
      • notez que, par contraste, le mot-clé Throw génère un script - erreur de terminaison qui annule le script entier .
      • Manipulation une déclaration de terminaison de l'erreur: Un try/catch du gestionnaire ou du trap déclaration peut être utilisé ( ne peut pas non-terminaison erreurs), mais note que même les déclaration terminaison d'erreurs par défaut n'empêche pas le reste de la script d'exécution. Comme pour les erreurs non terminantes , $? reflète $False si l'instruction précédente a déclenché une erreur de terminaison de l'instruction.

malheureusement, tous les cmdlets du noyau de PowerShell ne suivent pas ces règles :

  • bien que peu probable, New-TemporaryFile (PSv5+) signalerait une erreur non terminante si elle échouait, malgré le fait qu'elle n'acceptait pas l'entrée du pipeline et produisait seulement un objet de sortie-cela changera probablement dans v6, cependant: voir ce numéro GitHub .

  • Resume-Job 's help prétend que passer un type d'emploi non supporté (tel qu'un emploi créé avec Start-Job , qui n'est pas supporté, parce que Resume-Job ne s'applique qu'à workflow jobs) provoque une erreur de terminaison, mais ce n'est pas vrai à partir de PSv5.1.

10
répondu mklement0 2018-06-25 02:04:16

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

À l'utilisation de l'essayer...catch avec Write-Error :

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}
7
répondu Rynant 2012-02-15 14:39:39

ajout à réponse D'Andy Arismendi :

si Write-Error met fin au processus ou non dépend du paramètre $ErrorActionPreference .

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

"PowerShell comportement par défaut à l'égard d'erreurs, ce qui est à continuez sur erreur ...feels very VB6 "On Error Resume Next" - ish"

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

cependant, il fait des appels Write-Error se terminant.

pour utiliser Write-Error comme commande non-terminante indépendamment des autres paramètres d'environnement, vous pouvez utiliser paramètre commun -ErrorAction avec la valeur Continue :

 Write-Error "Error Message" -ErrorAction:Continue
4
répondu Michael Freidgeim 2017-05-23 12:34:48

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

0
répondu yzorg 2014-03-10 03:11:18