- 05/23/2020
- 14 minute pentru a citi
-
- j
- f
- s
Eroare de manipulare este doar o parte din viață când e vorba de scrierea de cod. Putem verifica și valida adeseacondiții pentru comportamentul așteptat. Când se întâmplă neașteptatul, apelăm la manipularea excepțiilor., Youcan mâner cu ușurință excepții generate de codul altor persoane sau puteți genera ownexceptions pentru alții să se ocupe.versiunea originală a acestui articol a apărut pe blogul scris de @KevinMarquette. Echipa ThePowerShell îi mulțumește lui Kevin că ne-a împărtășit acest conținut. Vă rugăm să verificați blogul său atPowerShellExplained.com.
terminologie de bază
trebuie să acoperim câțiva termeni de bază înainte de a intra în acesta.
excepție
o excepție este ca un eveniment care este creat atunci când manipularea normală a erorilor nu poate rezolva problema.,Încercarea de a împărți un număr la zero sau de a rămâne fără memorie sunt exemple de ceva care creează anexception. Uneori, autorul codului pe care îl utilizați creează excepții pentru anumite problemecând se întâmplă.când se întâmplă o excepție, spunem că o excepție este aruncată. Pentru a face față unei excepții aruncate, tutrebuie să-l prindă. Dacă o excepție este aruncată și nu este prinsă de ceva, scenariul se oprește.
stiva de apeluri
stiva de apeluri este lista funcțiilor care s-au apelat reciproc., Când se numește o funcție, aceastase adaugă la stivă sau în partea de sus a listei. Când funcția iese sau se întoarce, este eliminatădin stivă.
când este aruncată o excepție, acea stivă de apeluri este verificată pentru ca un handler de excepții să fie catchit.
erori de terminare și non-terminare
o excepție este, în general, o eroare de terminare. O excepție aruncată este fie prinsă, fie eatermină execuția curentă. În mod implicit, o eroare care nu se termină este generată de Write-Error
și adaugă o eroare la fluxul de ieșire fără a arunca o excepție.,
nu spun asta pentru că Write-Error
și alte non-încheiere erori nu declanșacatch
.
înghițirea unei excepții
Acest lucru este atunci când prindeți o eroare doar pentru a o suprima. Faceți acest lucru cu prudență, deoarece poate faceprobleme de fotografiere foarte dificile.
sintaxa de bază a comenzii
Iată o prezentare rapidă a sintaxei de manipulare a excepțiilor de bază utilizate în PowerShell.pentru a crea propriul nostru eveniment de excepție, aruncăm o excepție cu cuvântul cheie throw
.,
function Start-Something{ throw "Bad thing happened"}
aceasta creează o excepție de rulare care este o eroare de terminare. Este manipulat de un catch
în acalling funcție sau iese din script-ul cu un mesaj de genul asta.
Scrie-Eroare -ErrorAction Opri
– am spus că Write-Error
nu arunca o încheiere eroare în mod implicit. Dacă specificați-ErrorAction Stop
, Write-Error
generează o încheiere de eroare care pot fi manipulate cu uncatch
.,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
vă Mulțumim pentru Lee Dailey pentru amintindu despre utilizarea -ErrorAction Stop
în acest fel.
Cmdlet -ErrorAction Opri
Dacă specificați -ErrorAction Stop
pe orice funcție avansată sau cmdlet-ului, se pare tot Write-Error
declarații în încheiere erori care opresc executarea sau care pot fi manipulate de un catch
.,
Start-Something -ErrorAction Stop
Try/Catch
Pe cale de excepție de manipulare funcționează în PowerShell (și multe alte limbi) este că prima try
asection de cod și dacă se aruncă o eroare, puteți catch
ea. Iată o mostră rapidă.
scriptulcatch
rulează numai dacă există o eroare de terminare. Dacă try
execută corect, atunciacesta sare peste catch
.,
Încercați/în cele din Urmă
Uneori, nu aveți nevoie să se ocupe de o eroare, dar încă nevoie de unele cod pentru a executa dacă o exceptionhappens sau nu. Unfinally
script-ul face exact asta.
aruncați o privire la acest exemplu:
de fiecare dată când deschideți sau vă conectați la o resursă, ar trebui să o închideți. DacăExecuteNonQuery()
aruncă o excepție, conexiunea nu este închisă. Aici este același cod în interiorul unui try/finally
bloc.
în acest exemplu, conexiunea este închisă dacă există o eroare. De asemenea, este închis dacă există noerror., Scriptul finally
rulează de fiecare dată.
deoarece nu capturați excepția, aceasta este încă propagată în stiva de apeluri.
Try/Catch/în cele din Urmă
este perfect valabil pentru a utiliza catch
și finally
împreună. De cele mai multe ori veți folosi unul sau altul, dar puteți găsi scenarii în care le utilizați pe ambele.
$PSItem
acum, că am scos elementele de bază din drum, putem săpa puțin mai adânc.,
în catch
bloc, nu e automat o variabilă ($PSItem
sau $_
) de tip ErrorRecord
care conține detalii despre extensia de excepție. Aici este o imagine de ansamblu rapidă a unora dintre keyproperties.
pentru aceste exemple, am folosit o cale nevalidă în ReadAllText
pentru a genera această excepție.
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString ()
Acest lucru vă oferă cel mai curat mesaj pentru a utiliza în Logare și de ieșire generală., ToString()
isautomatically numit dacă$PSItem
este plasat în interiorul unui șir.
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
această proprietate conține informații suplimentare colectate de PowerShell despre funcția sau scriptunde a fost aruncată excepția. Aici este InvocationInfo
din excepția de probă care Icreed.
detalii aici ScriptName
, Line
cod și ScriptLineNumber
în cazul în care invocarea început.
$ PSItem.,ScriptStackTrace
această proprietate arată ordinea apelurilor funcționale care v-au dus la codul în care a fost generată excepția.
fac doar apeluri către funcții în același script, dar acest lucru ar urmări apelurile dacă ar fi implicate multiplescripts.
$ PSItem.Excepție
aceasta este excepția reală care a fost aruncată.
$ PSItem.Excepție.Mesaj
acesta este mesajul general care descrie excepția și este un bun punct de plecare atunci cândtroubleshooting. Cele mai multe excepții au un mesaj implicit, dar poate fi, de asemenea, setat la ceva personalizat atunci când excepția este aruncată.,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
Acest lucru este, de asemenea, mesajul s-a intors atunci când de asteptare $PSItem.ToString()
dacă nu a fost unul setat peErrorRecord
.
$ PSItem.Excepție.Excepțiile InnerException
pot conține excepții interioare. Acest lucru este adesea cazul când codul pe care îl apelațicaptează o excepție și aruncă o altă excepție. Excepția originală este plasată în interiornoua excepție.
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
voi revizui acest lucru mai târziu când voi vorbi despre re-aruncarea excepțiilor.
$ PSItem.Excepție.,StackTrace
acesta este StackTrace
pentru excepție. Am arătat un ScriptStackTrace
de mai sus, dar acesta este pentrucursurile la codul gestionat.
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 )
obțineți această urmă de stivă doar atunci când evenimentul este aruncat din Codul gestionat. Îl sun pe A.NETframework funcționează direct, astfel încât este tot ce putem vedea în acest exemplu. În general, atunci când căutați o urmărire a stivei, căutați unde se oprește codul dvs. și încep apelurile de sistem.
lucrul cu excepții
există mai multe excepții decât sintaxa de bază și proprietățile de excepție.,
capturarea excepțiilor tastate
puteți fi selectiv cu excepțiile pe care le prindeți. Excepțiile au un tip și puteți specificatipul de excepție pe care doriți să îl prindeți.
tipul De excepție este verificat pentru fiecare catch
bloc până când este găsit unul care se potrivește dvs. de excepție.Este important să realizăm că excepțiile pot moșteni de la alte excepții. În exemplul de mai sus,FileNotFoundException
moștenește de la IOException
. Deci, dacă IOException
a fost primul, atunci ar fi apelat în schimb., Doar un singur bloc de captură este invocat chiar dacă există mai multe meciuri.
Dacă am avea un System.IO.PathTooLongException
, IOException
s-ar potrivi dar dacă am avea unInsufficientMemoryException
atunci nimic nu s-ar prinde și l-ar propaga până stiva.este posibil să prindeți mai multe tipuri de excepții cu aceeași instrucțiune catch
.
Vă mulțumim /u/Sheppard_Ra
pentru a sugera această adăugare.
aruncarea excepții tastate
puteți arunca excepții tastate în PowerShell., În loc de asteptare throw
cu un șir de caractere:
throw "Could not find: $path"
Folositi o excepție accelerator de genul asta:
throw "Could not find: $path"
Dar trebuie să specificați un mesaj atunci când ai făcut-o așa.
de asemenea, puteți crea o nouă instanță a unei excepții care urmează să fie aruncată. Mesajul este opțional atunci când facețiacest lucru, deoarece sistemul are mesaje implicite pentru toate excepțiile încorporate.
throw ::new()throw ::new("Could not find path: $path")
dacă nu utilizați PowerShell 5.0 sau o versiune ulterioară, trebuie să utilizați abordarea mai veche New-Object
.,
folosind o excepție tastată, dvs. (sau alții) puteți prinde excepția după tipul menționat în secțiunea anterioară.
Scrie-Eroare -Excepție
putem adăuga aceste introduce excepții de la Write-Error
și putem catch
erori de exceptiontype. Folosiți Write-Error
ca în aceste exemple:
Atunci putem prinde o astfel de prognoze:
catch { Write-Log $PSItem.ToString()}
lista mare de .NET excepții
am compilat o listă cu ajutorul Reddit/r/PowerShell comunitate care containshundreds de .,NET excepții pentru a completa acest post.
- lista mare de excepții. net
încep prin a căuta acea listă de excepții care simt că ar fi potrivite pentru mysituation. Ar trebui să încercați să utilizați excepții în baza System
spațiu de nume.
excepțiile sunt obiecte
dacă începeți să utilizați o mulțime de excepții tastate, amintiți-vă că acestea sunt obiecte. Diferite excepțiiau constructori și proprietăți diferite., Dacă ne uităm la FileNotFoundExceptiondocumentation pentru System.IO.FileNotFoundException
, vom vedea că putem trece într-un mesaj și un filepath.
::new("Could not find file", $path)
Și are un FileName
proprietate care expune acea cale de fișier.
catch { Write-Output $PSItem.Exception.FileName}
ar trebui să consultați documentația.net pentru alți constructori și proprietăți obiect.
Re-arunca o excepție
Dacă tot ai de gând să faci în catch
bloc este throw
aceeași excepție, atunci nu catch
ea., Ar trebui doar catch
o excepție pe care intenționați să o gestionați sau să efectuați o acțiune atunci când se întâmplă.există momente în care doriți să efectuați o acțiune pe o excepție, dar re-arunca excepția sosomething aval poate face cu ea. Am putea scrie un mesaj sau să înregistrăm problema aproape de locul în care o descoperim, dar să rezolvăm problema în continuare.
catch{ Write-Log $PSItem.ToString() throw $PSItem}
destul de Interesant, o putem numi throw
din cadrul catch
și re-aruncă currentexception.,
catch{ Write-Log $PSItem.ToString() throw}
vrem să re-arunca excepția pentru a păstra informațiile de execuție originale ca script sursă și numărul liniei. Dacă aruncăm o nouă excepție în acest moment, se ascunde de unde a început excepția.
Re-a aruncat o nouă excepție,
Daca te prinde o excepție, dar vrei să arunci o alta, atunci ar trebui să cuibul originalexception în interiorul unul nou. Acest lucru permite cineva în jos stiva pentru a accesa ca$PSItem.Exception.InnerException
.
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$PSCmdlet.,ThrowTerminatingError()
un lucru care nu-mi place despre utilizarea throw
pentru prime excepții este că eroarea messagepoints la throw
declarație și indică faptul că linia este unde este problema.
având mesajul de eroare spune-mi că script-ul meu este rupt pentru că am sunat throw
pe linia 31 este un mesaj abad pentru utilizatorii de script-ul pentru a vedea. Nu le spune nimic util.Dexter Dhami a subliniat că pot folosi ThrowTerminatingError()
pentru a corecta acest lucru.,
Dacă presupunem că ThrowTerminatingError()
a fost numit în interiorul unei funcții numită Get-Resource
, atunci acesta este eroarea care ne-ar vedea.
vedeți cum indică funcția Get-Resource
ca sursă a problemei? Asta îi spune utilizatorului ceva util.
$PSItem
este un ErrorRecord
, putem folosi, de asemenea, ThrowTerminatingError
acest mod de a re-arunca.,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
aceasta schimbă sursa erorii în Cmdlet și ascunde interiorul funcției dvs. de lautilizatorii Cmdlet-ului dvs.Kirk Munro subliniază că unele excepții sunt doar erori de terminare atunci când sunt executate în interiorul unui bloctry/catch
. Iată exemplul pe care mi l-a dat care generează o divizare prin excepție de rulare zero.
function Start-Something { 1/(1-1) }
apoi invoca ca aceasta pentru a vedea genera eroarea și încă de ieșire mesajul.,
&{ Start-Something; Write-Output "We did it. Send Email" }
dar plasând același cod în interiorul unui try/catch
, vedem că se întâmplă altceva.
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
vedem eroarea devenind o eroare de terminare și nu de ieșire primul mesaj. Ceea ce nu-mi place despre asta este că puteți avea acest cod într-o funcție și acționează diferit dacă cineva utilizează un try/catch
.
nu am avut probleme cu acest lucru eu, dar este cazul de colț să fie conștienți de.
$ PSCmdlet.,ThrowTerminatingError() în interiorul try/catch
într-O nuanță de $PSCmdlet.ThrowTerminatingError()
este că se creează o încheiere de eroare în yourCmdlet dar se transformă într-un non-încheiere de eroare după ce acesta părăsește Cmdlet. Acest lucru lasă povara apelantului funcției dvs. pentru a decide cum să gestionați eroarea. Se pot întoarce în aterminating eroare prin utilizarea -ErrorAction Stop
sau numindu-l dintr-un try{...}catch{...}
.,
funcția Publică template-uri
Un ultim ia-o cum am avut-o cu conversația mea cu Kirk Munro a fost că el pune untry{...}catch{...}
în jurul valorii de fiecare begin
, process
și end
bloc în toate advancedfunctions. În aceste blocuri de captură generice, el are o singură linie folosind$PSCmdlet.ThrowTerminatingError($PSItem)
pentru a face față tuturor excepțiilor care părăsesc funcțiile sale.
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
deoarece totul este într-o declarațietry
în funcțiile sale, totul acționează în mod consecvent., Acest lucru oferă, de asemenea, erori curate utilizatorului final care ascunde codul intern de eroarea generată.
capcana
m-am concentrat petry/catch
aspect de excepții. Dar există o caracteristică moștenire trebuie să menționezînainte de a încheia asta.
Atrap
este plasat într-un script sau o funcție pentru a prinde toate excepțiile care se întâmplă în acest domeniu. Când se întâmplă o excepție, codul din trap
este executat și apoi codul normal continuă. Dacăse întâmplă mai multe excepții, atunci capcana este chemată de mai multe ori.,
eu personal nu a adoptat această abordare, dar eu pot vedea valoarea în admin sau controler script-uri carelog orice și toate excepțiile, apoi continuă să execute.
remarci de închidere
adăugarea unei gestionări corecte a excepțiilor la scripturile dvs. nu numai că le face mai stabile, dar vă face și mai ușor să depanați aceste excepții.
am petrecut mult timp vorbind throw
pentru că este un concept de bază atunci când vorbim despre exceptionhandling., De asemenea, PowerShell ne-a dat Write-Error
care gestionează toate situațiile în care ați folosithrow
. Deci nu credeți că trebuie să utilizați throw
după ce ați citit acest lucru.
Acum că am luat timp pentru a scrie despre manipulare excepție în acest detaliu, am de gând toswitch să folosească Write-Error -Stop
pentru a genera erori în codul meu. De asemenea, voi lua sfatul lui takeKirk și voi face ThrowTerminatingError
handler-ul meu de excepție goto pentru fiecare funcție.