Welcome to Our Website

Tout ce que vous vouliez savoir sur les exceptions

  • 05/23/2020
  • 14 minutes pour lire
    • j
    • f
    • s

la gestion des Erreurs est juste une partie de la vie quand il s’agit de l’écriture de code. Nous pouvons souvent vérifier et validerconditions pour le comportement attendu. Quand l’inattendu se produit, nous nous tournons vers la gestion des exceptions., Vous pouvez facilement gérer les exceptions générées par le code d’autres personnes ou vous pouvez générer vos propres exceptions pour que les autres puissent les gérer.

Remarque

La version originale de cet article est parue sur le blog écrit par @KevinMarquette. L’équipe de PowerShell remercie Kevin d’avoir partagé ce contenu avec nous. Veuillez consulter son blog atPowerShellExplained.com.

terminologie de base

Nous devons couvrir quelques termes de base avant de sauter dans celui-ci.

Exception

Une Exception est comme un événement qui est créé lorsque la gestion normale des erreurs ne peut pas traiter le problème.,Essayer de diviser un nombre par zéro ou manquer de mémoire sont des exemples de quelque chose qui crée une exception. Parfois, l’auteur du code que vous utilisez crée des exceptions pour certains problèmes lorsqu’ils se produisent.

à Lancer et à Attraper

Lorsqu’une exception se produit, nous disons qu’une exception est levée. Pour gérer une exception lancée, vousbesoin de l’attraper. Si une exception est levée et qu’elle n’est pas attrapée par quelque chose, le script stopsexecuting.

la pile d’appels

la pile d’appels est la liste des fonctions qui se sont appelées., Lorsqu’une fonction est appelée, elle est ajoutée à la pile ou en haut de la liste. Lorsque la fonction se termine ou revient, elle est supprimée de la pile.

Lorsqu’une exception est levée, cette pile d’appels est vérifiée pour qu’un gestionnaire d’exceptions puisse catchit.

erreurs de terminaison et de non-terminaison

Une exception est généralement une erreur de terminaison. Une exception levée est soit être interceptée, soit itterminates l’exécution en cours. Par défaut, une erreur non terminante est générée par Write-Erroret elle ajoute une erreur au flux de sortie sans lancer d’exception.,

je signale cela parce que Write-Error et d’autres erreurs non terminantes ne déclenchent pas lecatch.

avaler une exception

c’est quand vous attrapez une erreur juste pour la supprimer. Faites-le avec prudence car cela peut rendre les problèmes de tournage très difficiles.

syntaxe de commande de base

Voici un aperçu rapide de la syntaxe de base de gestion des exceptions utilisée dans PowerShell.

Jet

Pour créer notre propre événement d’exception, on lance une exception avec la balise throw mot clé.,

function Start-Something{ throw "Bad thing happened"}

Cela crée une exception d’exécution qui est une terminaison d’erreur. Il est géré par un catch dans la fonction acalling ou quitte le script avec un message comme celui-ci.

write-Error-ErrorAction Stop

j’ai mentionné queWrite-Error ne génère pas d’erreur de fin par défaut. Si vous spécifiez-ErrorAction Stop, Write-Errorgénère une terminaison d’erreur qui peuvent être traitées avec uncatch.,

Write-Error -Message "Houston, we have a problem." -ErrorAction Stop

Merci à Lee Dailey pour le rappel sur l’utilisation de la balise -ErrorAction Stop de cette façon.

Cmdlet-ErrorAction Stop

Si vous spécifiez-ErrorAction Stop sur n’importe quelle fonction ou cmdlet avancée, Toutes les instructionsWrite-Errorse terminent par des erreurs qui arrêtent l’exécution ou qui peuvent être gérées par uncatch.,

Start-Something -ErrorAction Stop

Try/Catch

La façon dont la gestion des exceptions travaille en PowerShell (et beaucoup d’autres langues) est que vous devez d’abord try asection de code, et si elle déclenche une erreur, vous pouvez catch c’. Voici un échantillon rapide.

le script catch ne s’exécute que s’il y a une erreur de fin. Si le try s’exécute correctement, alors il saute sur le catch.,

Try/Finally

parfois, vous n’avez pas besoin de gérer une erreur mais vous avez toujours besoin de code pour exécuter si un exceptionhappens ou non. Un scriptfinally fait exactement cela.

regardez cet exemple:

chaque fois que vous ouvrez ou vous connectez à une ressource, vous devez la fermer. Si l’exceptionExecuteNonQuery() throwsan, la connexion n’est pas fermée. Voici le même code dans un bloctry/finally.

dans cet exemple, la connexion est fermée en cas d’erreur. Il est également fermé s’il n’y a pas d’erreur., Le scriptfinally s’exécute à chaque fois.

parce que vous n’attrapez pas l’exception, elle est toujours propagée dans la pile d’appels.

Try/Catch/finally

Il est parfaitement valide pour utiliser catch et finally ensemble. La plupart du temps, vous utiliserez l’un oul’autre, mais vous pouvez trouver des scénarios où vous utilisez à la fois.

$PSItem

Maintenant que nous avons les bases, on peut creuser un peu plus profond.,

dans le bloccatch, il y a une variable automatique ($PSItemou$_) de typeErrorRecord qui contient les détails sur l’exception. Voici un aperçu rapide de certaines des propriétés keyproperties.

pour ces exemples, j’ai utilisé un chemin non valide dansReadAllText pour générer cette exception.

::ReadAllText( '\\test\no\filefound.log')

PSItem.ToString ()

cela vous donne le message le plus propre à utiliser dans la journalisation et la sortie générale., ToString() est automatiquement appelé si $PSItem est placé dans une chaîne.

catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}

PS PSItem.InvocationInfo

Cette propriété contient des informations supplémentaires collectées par PowerShell sur la fonction ou le script où l’exception a été levée. Voici le InvocationInfo de L’exemple d’exception Icreated.

Les détails importants ici montrent leScriptName, leLinedu code et leScriptLineNumber où l’appel a commencé.

PS PSItem.,ScriptStackTrace

Cette propriété affiche l’ordre des appels de fonction qui vous ont conduit au code où l’exception a été générée.

Je ne fais que des appels à des fonctions dans le même script, mais cela suivrait les appels si des multiplescripts étaient impliqués.

PS PSItem.Exception

c’est l’exception réelle qui a été levée.

PS PSItem.Exception.Message

ceci est le message général qui décrit l’exception et est un bon point de départ whentroubleshooting. La plupart des exceptions ont un message par défaut, mais peuvent également être définies sur quelque chose de personnalisé lorsque l’exception est levée.,

PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."

C’est aussi le message retourné lors de l’appel de $PSItem.ToString() si il n’y avait pas un jeu sur leErrorRecord.

PS PSItem.Exception.InnerException

Les Exceptions peuvent contenir des exceptions internes. C’est souvent le cas lorsque le code que vous appelezcatches une exception et lève une exception différente. L’exception d’origine est placée à l’intérieurla nouvelle exception.

PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.

je reviendrai plus tard quand je parlerai de relancer les exceptions.

PS PSItem.Exception.,StackTrace

ceci est leStackTrace pour l’exception. J’ai montré un ScriptStackTrace ci-dessus, mais celui-ci est pour les appels au code géré.

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 )

Vous n’obtenez cette trace de pile que lorsque l’événement est lancé à partir de code géré. Je suis d’appeler un .NETframework fonctionne directement, c’est tout ce que nous pouvons voir dans cet exemple. Généralement, lorsque vous recherchez une trace de pile, vous cherchez où votre code s’arrête et où les appels système commencent.

travailler avec des exceptions

Il y a plus aux exceptions que la syntaxe de base et les propriétés d’exception.,

capture des exceptions typées

Vous pouvez être sélectif avec les exceptions que vous attrapez. Les Exceptions ont un type et vous pouvez spécifierle type d’exception que vous souhaitez attraper.

le type d’exception est vérifié pour chaque bloc catch jusqu’à ce qu’un soit trouvé qui correspond à votre exception.It il est important de réaliser que les exceptions peuvent hériter d’autres exceptions. Dans l’exemple ci-dessus,FileNotFoundException hérite de IOException. Donc, si le IOException était le premier, alors il serait appelé à la place., Un seul bloc catch est invoqué même s’il y a plusieurs correspondances.

Si nous avions unSystem.IO.PathTooLongException, leIOExceptioncorrespondrait mais si nous avions unInsufficientMemoryException alors rien ne l’attraperait et il se propagerait dans la pile.

Attrape plusieurs types à la fois

Il est possible d’attraper plusieurs types d’exception avec la même instructioncatch.

Merci/u/Sheppard_Ra pour avoir suggéré cet ajout.

lancer des exceptions typées

Vous pouvez lancer des exceptions typées dans PowerShell., Au lieu d’appeler throw avec une chaîne de caractères:

throw "Could not find: $path"

l’Utilisation d’une exception d’accélérateur comme ceci:

throw "Could not find: $path"

Mais vous devez spécifier un message lorsque vous le faire de cette façon.

Vous pouvez également créer une nouvelle instance de la levée d’une exception. Le message est facultatif lorsque vous le faites car le système a des messages par défaut pour toutes les exceptions intégrées.

throw ::new()throw ::new("Could not find path: $path")

Si vous n’êtes pas à l’aide de PowerShell 5.0 ou supérieur, vous devez utiliser l’ancienne New-Object approche.,

en utilisant une exception typée, vous (ou d’Autres) pouvez attraper l’exception par le type comme mentionné dans la section précédente.

Write-Error -Exception

Nous pouvons ajouter ces tapé exceptions Write-Error et nous pouvons encore catch les erreurs par exceptiontype. Utilisez Write-Error comme dans ces exemples:

alors nous pouvons l’attraper comme ceci:

catch { Write-Log $PSItem.ToString()}

la grande liste des exceptions .net

j’ai compilé une liste principale avec l’aide de la communauté Reddit/r / PowerShell qui contient des centaines de.,Exceptions nettes pour compléter ce poste.

  • La grande liste des exceptions.net

je commence par rechercher dans cette liste des exceptions qui semblent convenir à la mysituation. Vous devriez essayer d’utiliser des exceptions dans l’espace de noms System de base.

les Exceptions sont des objets

Si vous commencez à utiliser beaucoup d’tapé exceptions, n’oubliez pas qu’ils sont des objets. Différentes exceptionshave différents constructeurs et propriétés., Si nous regardons la FileNotFoundExceptiondocumentation pour System.IO.FileNotFoundException, nous voyons que nous pouvons passer un message et un chemin de fichier.

::new("Could not find file", $path)

et il a une propriétéFileName qui expose ce chemin de fichier.

catch { Write-Output $PSItem.Exception.FileName}

Vous devriez consulter la documentation.Net pour d’autres constructeurs et propriétés d’objet.

relancer une exception

Si tout ce que vous allez faire dans votre bloccatchestthrowla même exception, alors ne le faites pascatch., Vous devez uniquementcatch une exception que vous prévoyez de gérer ou d’effectuer une action lorsque ithappens.

Il y a des moments où vous voulez effectuer une action sur une exception mais relancer l’exception sosomething en aval peut y faire face. Nous pourrions écrire un message ou enregistrer le problème près de l’endroit où nous le découvrons, mais gérer le problème plus haut dans la pile.

catch{ Write-Log $PSItem.ToString() throw $PSItem}

assez intéressant, nous pouvons appelerthrowà partir ducatch et il relance l’exception currentexception.,

catch{ Write-Log $PSItem.ToString() throw}

nous voulons relancer l’exception pour préserver les informations d’exécution d’origine comme le script source et le numéro de ligne. Si nous lançons une nouvelle exception à ce stade, elle cache l’endroit où l’exception a commencé.

relancer une nouvelle exception

Si vous attrapez une exception mais que vous souhaitez en lancer une autre, vous devez imbriquer l’exception originalexception dans la nouvelle. Cela permet à quelqu’un en bas de la pile d’y accéder en tant que$PSItem.Exception.InnerException.

catch{ throw ::new('Could not access field',$PSItem.Exception)}

let PSCmdlet.,ThrowTerminatingError ()

la seule chose que je n’aime pas à propos de l’utilisation de throw pour les exceptions brutes est que le message d’erreur pointe à l’instruction throw et indique que line est l’endroit où se trouve le problème.

avoir le message d’erreur me dire que mon script est cassé parce que j’ai appeléthrow sur la ligne 31 est un message abad pour les utilisateurs de votre script à voir. Il ne dit rien d’utile.

Dexter Dhami souligné que je peux utiliser ThrowTerminatingError() pour corriger cela.,

Si nous supposons que ThrowTerminatingError() a été appelée à l’intérieur d’une fonction appelée Get-Resource, puiscette est l’erreur que nous voudrions voir.

voyez-vous comment elle pointe vers la fonctionGet-Resource comme source du problème? Cela dit à l’utilisateur quelque chose d’utile.

Car $PSItem est une ErrorRecord, nous pouvons aussi utiliser des ThrowTerminatingError cette façon de re-jeter.,

catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}

cela change la source de l’erreur en applet de commande et masque les éléments internes de votre fonction aux utilisateurs de votre applet de commande.

Try peut créer des erreurs de terminaison

Kirk Munro souligne que certaines exceptions ne sont terminatrices que lorsqu’elles sont exécutées dans un bloctry/catch. Voici l’exemple qu’il m’a donné qui génère une division par zéro exception d’exécution.

function Start-Something { 1/(1-1) }

Puis l’appeler comme ça pour voir générer l’erreur et encore sortie le message.,

&{ Start-Something; Write-Output "We did it. Send Email" }

mais en plaçant ce même code dans un try/catch, nous voyons quelque chose d’autre se produire.

try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}

Nous voyons l’erreur devenir une erreur de terminaison et ne pas sortir le premier message. Ce que je n’aime pas à propos de celui-ci, c’est que vous pouvez avoir ce code dans une fonction et qu’il agit différemment si quelqu’un utilise un try/catch.

je n’ai pas rencontré de problèmes avec cela moi-même, mais c’est un cas de coin dont il faut être conscient.

let PSCmdlet.,ThrowTerminatingError () dans try/catch

Une nuance de $PSCmdlet.ThrowTerminatingError() est qu’il crée une erreur de terminaison dans yourCmdlet mais il se transforme en une erreur de non-terminaison après avoir quitté votre Cmdlet. Cela laisse le fardeau à l’appelant de votre fonction de décider comment gérer l’erreur. Ils peuvent le transformer en erreur aterminating en utilisant -ErrorAction Stop ou en l’appelant depuis un try{...}catch{...}.,

modèles de fonction publique

Un dernier moyen que j’ai eu avec ma conversation avec Kirk Munro était qu’il place untry{...}catch{...} autour de chaquebegin,process Etend bloquer dans toutes ses fonctions avancées. Dans ces blocs catch génériques, il a une seule ligne utilisant$PSCmdlet.ThrowTerminatingError($PSItem) pour gérer toutes les exceptions quittant ses fonctions.

function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}

parce que tout est dans une instructiontry dans ses fonctions, tout agit de manière cohérente., Cela donne également des erreurs propres à l’utilisateur final qui masque le code interne de l’erreur générée.

Piège

je me suis concentré sur le try/catch aspect des exceptions. Mais il y a une fonctionnalité héritée que je dois mentionner avant que nous terminions cela.

trap est placé dans un script ou une fonction pour intercepter toutes les exceptions qui se produisent dans cette portée. Quandune exception se produit, le code dans le trap est exécuté, puis le code normal continue. Ifmultiple exceptions se produisent, alors le piège est appelé encore et encore.,

personnellement, je n’ai jamais adopté cette approche, mais je peux voir la valeur dans les scripts admin ou controller qui enregistrent toutes les exceptions, puis continuent à s’exécuter.

remarques finales

L’ajout d’une gestion appropriée des exceptions à vos scripts les rend non seulement plus stables, mais vous permet également de résoudre plus facilement ces exceptions.

j’ai passé beaucoup de temps à parlerthrow parce que c’est un concept de base quand on parle de exceptionhandling., PowerShell nous a également donné Write-Error qui gère toutes les situations où vous utiliseriezthrow. Donc, ne pensez pas que vous devez utiliser throw après avoir lu ceci.

maintenant que j’ai pris le temps d’écrire sur la gestion des exceptions dans ce détail, je vais passer à l’utilisation deWrite-Error -Stop pour générer des erreurs dans mon code. Je vais également prendre les conseils de takeKirk et faire de ThrowTerminatingError mon gestionnaire d’exceptions goto pour chaque fonction.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *