Welcome to Our Website

Tot ce ai vrut să știi despre excepții

  • 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-Errorgenerează 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-Errordeclaraț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 ErrorRecordcare 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 catchea., 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.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *