web-dev-qa-db-fra.com

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

En regardant un script Get-WebFile sur PoshCode, http://poshcode.org/3226 , j'ai remarqué cet inconvénient étrange:

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

Quelle est la raison de ceci par opposition à ce qui suit?

$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 devez utiliser Write-Error pour les erreurs non-terminantes et Throw pour les erreurs terminales. Il me semble donc que vous ne devriez pas utiliser Write-Error suivi de Return. Y a-t-il une différence?

131
Bill Barry

_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 produit par contre ce que l’on appelle une erreur de terminaison. Si vous utilisez throw, le pipeline et/ou la boucle de courant seront terminés. En fait, toute exécution sera terminée sauf si vous utilisez une structure trap ou _try/catch_ pour traiter l'erreur de terminaison.

Il y a une chose à noter, si vous définissez $ErrorActionPreference_ SUR _"Stop" et utilisez _Write-Error_, ce sera produit une erreur de terminaison .

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 ait voulu arrêter l'exécution de cette fonction et afficher un message d'erreur à l'écran, mais ne souhaitait pas que le script complet cesse de s'exécuter. L'auteur du script aurait pu utiliser throw, mais cela signifierait que vous devrez utiliser un _try/catch_ lors de l'appel de la fonction.

return quittera la portée 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
_

Il faut utiliser return avec _ForEach-Object_. Le traitement ne sera pas interrompu comme on pourrait s'y attendre.

Plus d'information:

165
Andy Arismendi

La principale différence entre la cmdlet Write-Error et le mot clé throw dans PowerShell est que l'ancien est simplement affiche du texte dans le flux d'erreur standard (stderr) , alors que ce dernier réellement termine le traitement de la commande ou de la fonction en cours d'exécution, qui est alors géré par PowerShell par envoi d'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 = [string]"..."
Write-Error $URL_Format_Error
return

Dans cet exemple, le mot clé return a été ajouté à explicitement pour arrêter l'exécution du script après l'envoi du message d'erreur à la console. Dans le deuxième 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
16
Enrico Campidoglio

Important : Il y a 2 types d'erreurs de terminaison , que les rubriques d'aide actuelles malheureusement associent :

  • instruction - erreurs de terminaison , telles qu'elles sont signalées par les applets de commande dans certaines situations non récupérables et par les expressions dans lesquelles a. Une exception NET/une erreur d'exécution PS se produit; seule l'instruction est terminée et l'exécution du script se poursuit par défaut .

  • script - erreurs de terminaison , déclenchées soit par Throw, soit en escaladant l'une des autres erreurs types via error-action variable de préférence/valeur de paramètre Stop.
    Sauf s’ils sont attrapés, ils mettent fin au thread d’exécution en cours (c’est-à-dire non seulement au script actuel, mais également à tous ses appelants, le cas échéant).

Pour un aperçu complet de la gestion des erreurs de PowerShell, voir problème de documentation GitHub .

Le reste de cet article se concentre sur les erreurs non-terminantes par rapport aux instructions .


Pour compléter les réponses utiles existantes en mettant l’accent sur le cœur de la question: Comment choisissez-vous de signaler une déclaration - erreur de terminaison ou ?

Rapport d'erreurs de la cmdlet contient des instructions utiles; laissez-moi essayer un résumé pragmatique :

L'idée générale derrière les erreurs non terminales est d'autoriser le traitement "tolérant aux pannes" de grands ensembles d'entrées : l’échec du traitement d’un sous-ensemble des objets d’entrée ne doit pas (par défaut) interrompre le processus - potentiellement long - dans son ensemble, ce qui vous permet d’inspecter les erreurs et de retraiter uniquement le objets en échec plus tard - tels que signalés via les enregistrements d'erreur collectés dans la variable automatique $Error.

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

    • accepte plusieurs objets d'entrée , via les paramètres d'entrée de pipeline et/ou de tableau, ET
    • des erreurs se produisent pour des objets d'entrée spécifiques , et
    • ces erreurs n'empêchent pas le traitement d'autres objets d'entrée EN PRINCIPE ( de manière situationnelle , il ne peut rester aucun objet d'entrée et/ou les objets d'entrée précédents 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 provoque pas le paramétrage de $? sur $False dans le portée de l'appelant - voir ce problème de GitHub ).
      • Traitement d'une erreur non-terminante: $? vous indique si la commande la plus récente a signalé au moins une erreur non-terminante. ____.]
        • Ainsi, $? étant $False peut signifier que un sous-ensemble (non vide) d'objets d'entrée n'a pas été traité correctement, éventuellement l'ensemble.
        • La variable de préférence $ErrorActionPreference et/ou le paramètre d'applet de commande commun -ErrorAction peuvent modifier le comportement des erreurs non-terminantes (uniquement) en termes de comportement de sortie et indiquer si les erreurs non-terminales doivent être escaladées . ) script - en terminant.
  • Signalez une erreur STATEMENT-TERMINATING dans dans tous les autres cas .

    • Notamment, si une erreur survient dans une applet de commande/fonction avancée qui accepte uniquement un objet d'entrée SINGLE ou NO et génère NO ou un objet de sortie SINGLE ou prend la saisie de paramètre uniquement et les valeurs de paramètre fournies empêchent toute opération significative.
      • Dans les fonctions avancées, vous devez utiliser $PSCmdlet.ThrowTerminatingError() pour générer une erreur mettant fin aux instructions.
      • Notez que, par contraste, le mot clé Throw génère un ( script - erreur de fin qui annule le script complet (techniquement: le courant fil ).
      • Traitement d'une erreur de terminaison d'instruction: Un gestionnaire try/catch ou une instruction trap peut être utilisé (ce qui ne peut pas être utilisé avec erreurs non-terminantes ), mais notez que même instruction - les erreurs finales par défaut n'empêchent pas le reste du script de s'exécuter. Comme pour les erreurs non terminantes , $? reflète $False si l'instruction précédente a déclenché une erreur mettant fin à l'instruction.

Malheureusement, toutes les applets de commande principales de PowerShell ne sont pas soumises à ces règles :

  • Bien que cela soit peu probable, New-TemporaryFile (PSv5 +) signalerait une erreur non-terminante si elle échouait, même si elle n'acceptait pas l'entrée de pipeline et ne produisait qu'un objet de sortie - cela changerait probablement dans la v6 cependant: voir ce numéro de GitHub .

  • L'aide de Resume-Job déclare que le transfert d'un type de travail non pris en charge (tel qu'un travail créé avec Start-Job, qui n'est pas pris en charge, car Resume-Job s'applique uniquement au flux de travail ) provoque une erreur de terminaison, mais ce n'est pas vrai à partir de PSv5.1.

13
mklement0

Write-Error permet à l'utilisateur de la fonction de supprimer le message d'erreur avec -ErrorAction SilentlyContinue (ou bien -ea 0). Bien que throw nécessite un try{...} catch {..}

Pour utiliser un essai ... attraper avec Write-Error:

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

Ajout à réponse de Andy Arismendi :

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

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

"Le comportement par défaut de PowerShell en ce qui concerne les erreurs, qui doivent continuer sur les erreurs ... semble très VB6" Sur erreur, Reprendre suivant "-ish"

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

Cependant, les appels Write-Error se terminent.

Pour utiliser Write-Error en tant que commande qui ne se termine pas, quels que soient les autres paramètres d'environnement, vous pouvez utiliser paramètre commun-ErrorAction avec la valeur Continue:

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

Si votre lecture du code est correcte, alors 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.

0
yzorg