- 05/23/2020
- 14 minuuttia lukea
-
- j
- f
- s
Virheiden käsittely on vain osa elämää, kun se tulee kirjallisesti koodi. Voimme usein tarkistaa ja validaattiedellytykset odotetun käyttäytymisen. Kun odottamaton tapahtuu, siirrymme poikkeuskäsittelyyn., Voit helposti käsitellä poikkeuksia syntyy muiden ihmisten koodi tai voit luoda oman ownexceptions toisten käsitellä.
Huom.
alkuperäinen versio tämän artikkelin ilmestyi blogi kirjoittanut @KevinMarquette. ThePowerShell-tiimi kiittää Keviniä tämän sisällön jakamisesta kanssamme. Tutustu hänen blogi atPowerShellExplained.com.
Perus terminologiaa
– Meidän täytyy kattaa joitakin perus ehdot, ennen kuin voimme hypätä tämän yhden.
poikkeus
poikkeus on kuin tapahtuma, joka syntyy, kun normaali virheenkäsittely ei voi käsitellä asiaa.,Yrittää jakaa numero on nolla tai loppumassa muisti ovat esimerkkejä jotain, joka luo lisäksi. Joskus koodin tekijä luo poikkeuksia tietyille kysymyksille, kun niitä tapahtuu.
heitto ja saalis
kun poikkeus tapahtuu, sanomme, että poikkeus heitetään. Heitetyn poikkeuksen käsittelemiseksi sinun on pyydettävä sitä. Jos poikkeus heitetään, ja se ei ole kiinni jotain, käsikirjoitus stopsexecuting.
kutsupino
kutsupino on luettelo toisiaan soittaneista funktioista., Kun funktio kutsutaan, itgets lisätään pinoon tai listan alkuun. Kun toiminto poistuu tai palaa, se poistetaan pinosta.
Kun poikkeus heitetään, että call stack on valittu, jotta poikkeus handler catchit.
Päättämisestä ja ei-päättämisestä virheitä
poikkeus on yleensä päättämisestä virhe. Heitetty poikkeus joko jää kiinni tai se keskeyttää nykyisen teloituksen. Oletuksena, ei-päättämisestä virhe syntyy Write-Error
ja lisää virheilmoituksen output stream ilman heittää poikkeus.,
tämä on koska Write-Error
ja muut ei-päättämisestä virheet eivät laukaisecatch
.
niellessä poikkeuksen
tällöin sattuu virhe vain sen tukahduttamiseksi. Tee tämä varoen, koska se voi tehdäkeskeinen kysymyksiä erittäin vaikeaa.
Basic command syntax
Tässä on nopea katsaus perus-poikkeusten käsittely-syntaksia käytetään PowerShell.
heitä
luodaksemme Oman poikkeustapahtumamme, heitämme poikkeuksen throw
avainsanalla.,
function Start-Something{ throw "Bad thing happened"}
Tämä luo runtime poikkeus, että on päättämisestä virhe. Se hoitaa catch
vuonna acalling toiminnon tai poistuu script viestin, kuten tämä.
Write-Error-ErrorAction Lopettaa
mainitsin, että Write-Error
ei heittää päättämisestä virhe oletusarvoisesti. Jos määrität-ErrorAction Stop
, Write-Error
luo päättämisestä virhe, joka voi käsitelläcatch
.,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
Kiitos Lee Dailey muistuttaa siitä, käyttäen -ErrorAction Stop
tällä tavalla.
Cmdlet -ErrorAction Lopettaa
Jos määrität -ErrorAction Stop
tahansa kehittyneitä toiminto tai-cmdlet-komentoa, se kääntyy kaikki Write-Error
lausunnot osaksi päättämisestä virheitä, että lopettaa suorittamiseen tai jotka voidaan hoitaa catch
.,
Start-Something -ErrorAction Stop
Try/Catch
tapa, poikkeusten käsittely toimii PowerShell (ja monet muut kielet) on että ensin try
asection koodia, ja jos se heittää virhe, voit catch
sitä. Tässä on nopea näyte.
catch
script toimii vain jos on päättämisestä virhe. Jos try
suorittaa oikein, thenit hyppää yli catch
.,
Kokeilla/Vihdoin
Joskus sinun ei tarvitse käsitellä virheen, mutta silti on joitakin koodi, joka suoritetaan, jos exceptionhappens tai ei. A finally
script tekee juuri niin.
Katso tästä esimerkistä:
milloin tahansa avaat tai liität resurssiin, se kannattaa sulkea. Jos ExecuteNonQuery()
throwsan poikkeus, yhteys ei ole suljettu. Tässä on sama koodi try/finally
lohkon sisällä.
tässä esimerkissä yhteys suljetaan, jos siinä on virhe. Se on myös suljettu, jos ei ole virhe., finally
skripti kulkee joka kerta.
Koska et ole kiinni poikkeus, se saa edelleen levittävät puhelun pino.
Try/Catch/Lopuksi
Se on täysin voimassa, käyttää catch
ja finally
yhdessä. Useimmiten käytät jompaakumpaa, mutta voit löytää skenaarioita, joissa käytät molempia.
$PSItem
nyt kun saimme perusasiat pois alta, voimme kaivaa hieman syvemmälle.,
Sisälle catch
lohkon, on automaattinen muuttuja ($PSItem
tai $_
) tyyppi ErrorRecord
, joka sisältää tietoja lukuun ottamatta. Tässä on nopea katsaus joihinkin näppäimistöihin.
näissä esimerkeissä käytin virheellistä polkua ReadAllText
luodakseni tämän poikkeuksen.
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString()
Tämä antaa sinulle puhtain viesti käytettäväksi hakkuut ja yleinen lähtö., ToString()
isautomatically kutsutaan jos $PSItem
on sijoitettu merkkijono.
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
Tämä ominaisuus sisältää muita tietoja kerätään PowerShell noin toiminto tai scriptwhere poikkeuksena oli heitetty. Tässä on InvocationInfo
näytteestä poikkeus, että Icreated.
tärkeät yksityiskohdat täällä näyttää ScriptName
, Line
koodi ja ScriptLineNumber
missä vetoaminen alkoi.
$PSItem.,ScriptStackTrace
Tämä ominaisuus näyttää funktiokutsujen järjestyksen, joka sai sinut koodiin, jossa poikkeus ongeneroitu.
– olen vain soittamiseen toimintoja sama kirjoitus, mutta tämä olisi seurata puhelut, jos multiplescripts olivat mukana.
$PSItem.Poikkeus
Tämä on todellinen poikkeus, joka heitettiin.
$PSItem.Poikkeus.Viesti
Tämä on yleinen viesti, joka kuvaa ottamatta ja on hyvä lähtökohta whentroubleshooting. Useimmissa poikkeuksissa on oletusviesti, mutta ne voidaan myös asettaa johonkin mukautettuun, kun poikkeus heitetään.,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
– Tämä on myös viesti palasi soitettaessa $PSItem.ToString()
jos ei ole yksi asetettuErrorRecord
.
$PSItem.Poikkeus.InnerException
poikkeukset voivat sisältää sisäisiä poikkeuksia. Tämä on usein silloin, kun koodin olet callingcatches poikkeus ja heittää eri poikkeus. Alkuperäinen poikkeus on sijoitettu uuteen poikkeukseen.
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
aion palata tähän myöhemmin, kun puhun uudelleen heittää poikkeuksia.
$PSItem.Poikkeus.,StackTrace
Tämä on StackTrace
poikkeusta. Näytin ScriptStackTrace
edellä, mutta tämä on forthe puhelut hallitun koodin.
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 )
saat tämän pinon jäljen vain, kun tapahtuma heitetään hallinnoidusta koodista. Soitan a: han .NETframework toiminto suoraan niin, että on kaikki mitä voimme nähdä tässä esimerkissä. Yleensä kun katsot pinojälkeä, etsit mihin koodisi pysähtyy ja järjestelmäpuhelut alkavat.
Työskentely poikkeukset
Siellä on enemmän poikkeuksia kuin syntaksi ja poikkeus ominaisuuksia.,
kirjoitettujen poikkeusten pyydystäminen
voit olla valikoiva pyydystämiäsi poikkeuksia lukuun ottamatta. Poikkeuksilla on tyyppi ja voit täsmentää poikkeustyypin, jonka haluat pyydystää.
poikkeuksen tyyppi on valittu kullekin catch
lohkon, kunnes yksi löytyy, joka vastaa poikkeus.On tärkeää ymmärtää, että poikkeuksia voi periä muilta poikkeuksia. Yllä olevassa esimerkissäFileNotFoundException
perii IOException
. Joten jos IOException
oli ensin, sitten kävisi nimeltään sijaan., Vain yhteen pyyntilohkoon vedotaan, vaikka otteluita olisi useita.
Jos meillä oli System.IO.PathTooLongException
, IOException
sopisi, mutta jos meillä oliInsufficientMemoryException
sitten mikään ei olisi kiinni se ja se levittää jopa pino.
Kiinni useita tyyppejä samanaikaisesti
on mahdollista, kiinni useita lukuun ottamatta tyyppejä samalla catch
lausunto.
Kiitos /u/Sheppard_Ra
ehdottaa tämän lisäksi.
heitto kirjoitetut poikkeukset
voit heittää kirjoitetut poikkeukset Powershelliin., Sen sijaan, kutsuen throw
merkkijono:
throw "Could not find: $path"
Käytä poikkeus kiihdytin, kuten tämä:
throw "Could not find: $path"
Mutta sinun täytyy määrittää viestin, kun teet niin.
voit myös luoda uuden poikkeuksen heitettäväksi. Viesti on valinnainen, kun olet tehdä tämä, koska järjestelmä on oletuksena viestit kaikki sisäänrakennettu poikkeuksia.
throw ::new()throw ::new("Could not find path: $path")
Jos et käytä PowerShell 5.0 tai uudempi, sinun täytyy käyttää vanhempaa New-Object
lähestymistapaa.,
koneella kirjoitettua poikkeusta käyttäen sinä (tai muut) voit pyydystää poikkeuksen tyypin mukaan sellaisena kuin se on mainittu edellisessä jaksossa.
Write-Error-Poikkeus
– Voimme lisätä nämä kirjoitetaan poikkeukset Write-Error
ja voimme silti catch
virheitä, joita exceptiontype. Käyttää Write-Error
kuten näissä esimerkeissä:
Sitten voimme saada sen kiinni, kuten tämä:
catch { Write-Log $PSItem.ToString()}
iso lista .NET poikkeukset
olen koonnut listaa avulla Reddit/r/PowerShell yhteisö, joka containshundreds kannalta .,Nettomääräiset poikkeukset täydentämään tätä virkaa.
- iso lista .NET poikkeukset
aloitan hakemalla että lista poikkeuksia, jotka tuntuu, että ne olisi hyvä sovi mysituation. Base System
nimiavaruudessa kannattaa yrittää käyttää poikkeuksia.
poikkeukset ovat objekteja
jos alat käyttää paljon kirjoitettuja poikkeuksia, muista, että ne ovat objekteja. Eri poikkeuksetovat erilaisia rakentajia ja ominaisuuksia., Jos katsomme FileNotFoundExceptiondocumentation varten System.IO.FileNotFoundException
näemme, että voimme välittää viestin ja filepath.
::new("Could not find file", $path)
Ja se on FileName
ominaisuus, joka altistaa sen tiedoston polku.
catch { Write-Output $PSItem.Exception.FileName}
kannattaa tutustua.Net-dokumentaatioon muiden rakentajien ja objektien ominaisuuksista.
Uudelleen heittää poikkeus
Jos kaikki aiot tehdä oman catch
lohko on throw
sama poikkeus, älä sitten catch
sitä., Sinun pitäisi vain catch
poikkeus, että aiot käsitellä tai suorittaa joitakin toimia, kun ithappens.
on aikoja, jolloin haluat suorittaa toiminnon poikkeuksella, mutta heittää uudelleen poikkeuksen sosomething alavirtaan voi käsitellä sitä. Voisimme kirjoittaa viestin tai kirjata ongelman lähelle sitä, missä wediscover se, mutta käsitellä asiaa pidemmälle pinon.
catch{ Write-Log $PSItem.ToString() throw $PSItem}
Mielenkiintoista kyllä, voimme kutsua throw
sisällä catch
ja uudelleen heittää currentexception.,
catch{ Write-Log $PSItem.ToString() throw}
haluamme uudelleen heittää poikkeus säilyttää alkuperäisen suorituksen tiedot, kuten lähde scriptand rivin numero. Jos tässä vaiheessa heitämme uuden poikkeuksen, se piiloutuu sinne, mistä poikkeus alkoi.
heittää uuden poikkeuksen uudelleen
jos nappaat poikkeuksen, mutta haluat heittää toisen, niin sinun pitäisi pesiytyä uuteen poikkeukseen. Tämän avulla joku alas pino käyttää sitä$PSItem.Exception.InnerException
.
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$PSCmdlet.,ThrowTerminatingError()
yksi asia, että en pidä siitä, käyttäen throw
raaka-poikkeuksia on, että virhe messagepoints klo throw
julkilausuman ja ilmaisee, että rivi on missä ongelma on.
Ottaa virhe viesti kertoa minulle, että minun kirjoitus on rikki, koska soitin throw
rivillä 31 on abad viesti käyttäjille käsikirjoitus nähdä. Se ei kerro heille mitään hyödyllistä.
Dexter Dhami huomautti, että voin käyttää ThrowTerminatingError()
korjaamaan, että.,
Jos oletamme, että ThrowTerminatingError()
oli nimeltään sisällä funktion kutsutaan Get-Resource
, thenthis on virhe, että haluamme nähdä.
haluatko nähdä, miten se viittaa Get-Resource
toiminto, koska ongelman lähde? Se kertoo theuserille jotain hyödyllistä.
Koska $PSItem
on ErrorRecord
voimme myös käyttää ThrowTerminatingError
tällä tavalla uudelleen heittää.,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
Tämä muuttaa lähde virhe-Cmdlet-komentoa ja piilottaa sisäosat toiminto tarkasteltavien käyttäjille-Cmdlet-komentoa.
Kokeile luoda päättämisestä virheitä
Kirk Munro huomauttaa, että joitakin poikkeuksia lukuun ottamatta ovat vain päättämisestä virheitä, kun suoritetaan sisälletry/catch
lohko. Tässä on esimerkki, jonka hän antoi minulle, joka luo kuilun nolla runtime poikkeus.
function Start-Something { 1/(1-1) }
Sitten vedota sen näin nähdä se tuottaa virhe ja silti tulostaa viestin.,
&{ Start-Something; Write-Output "We did it. Send Email" }
Mutta asettamalla, että sama koodi sisälle try/catch
näemme jotain muuta tapahtuu.
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
näemme, että virhe tulee päättämisestä virhe ja ei lähtö ensimmäinen viesti. Mitä en likeabout tämä yksi on, että voit saada tämän koodin toiminto, ja se toimii eri tavalla, jos someoneis käyttäen try/catch
.
en ole itse törmännyt tämän kanssa ongelmiin, mutta se on nurkkatapaus olla tietoinen.
$PSCmdlet.,ThrowTerminatingError() sisällä try/catch
Yksi vivahde $PSCmdlet.ThrowTerminatingError()
on, että se luo päättämisestä virhe sisällä yourCmdlet mutta se muuttuu ei-päättämisestä virhe jälkeen se lähtee-Cmdlet-komentoa. Tämä jättää burdenon soittaja toiminnon päättää, miten käsitellä virhe. He voivat muuttaa sen takaisin aterminating virhe käyttämällä -ErrorAction Stop
tai soittamalla sen sisällä try{...}catch{...}
.,
Julkinen toiminta malleja
Yksi viimeinen keino, minulla oli minun keskustelun Kirk Munro oli, että hän asettaatry{...}catch{...}
noin joka begin
, process
ja end
lohkon kaikki hänen advancedfunctions. Näissä generic kiinni lohkoja, hän on yhdellä rivillä käyttäen$PSCmdlet.ThrowTerminatingError($PSItem)
käsitellä kaikkia poikkeuksia, jolloin hänen toiminnot.
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
Koska kaikki on try
lausuman sisällä hänen toiminnot, kaikki toimii johdonmukaisesti., Tämä antaa myös puhtaat virheet loppukäyttäjälle, joka piilottaa sisäisen koodin syntyvältä virheeltä.
Ansa
olen keskittynyt try/catch
osa poikkeuksia. Mutta tarvitsen yhden perintöominaisuuden, ennen kuin lopetamme tämän.
trap
asetetaan skripti tai funktio ottaa kiinni kaikki poikkeukset, jotka tapahtuvat, että soveltamisalaa. Kun poikkeus tapahtuu, suoritetaan trap
koodi, jonka jälkeen normaali koodi jatkuu. Jos poikkeuksia tapahtuu, niin ansa kutsutaan yhä uudelleen.,
itse en koskaan omaksunut tätä lähestymistapaa, mutta näen arvon admin-tai controller-skripteissä, jotka kirjaavat kaikki ja kaikki poikkeukset, sitten edelleen suorittaa.
loppuhuomautukset
asianmukaisen poikkeusten käsittelyn lisääminen skripteihisi paitsi tekee niistä vakaampia, myös tekee niiden vianmäärityksestä helpompaa.
– olen viettänyt paljon aikaa puhumalla throw
koska se on keskeinen käsite, kun puhutaan exceptionhandling., PowerShell antoi meille myös Write-Error
joka hoitaa kaikki tilanteet, joissa käyttäisitthrow
. Joten älä usko, että sinun tarvitsee käyttää throw
luettuasi tämän.
Nyt, että olen ottanut aikaa kirjoittaa siitä, poikkeusten käsittely tässä yksityiskohtaisesti, aion toswitch yli käyttäen Write-Error -Stop
tuottaa virheitä minun koodi. Menen myös takekirkin neuvoihin ja teen ThrowTerminatingError
minun Goto-poikkeuskäsittelijäni jokaiselle funktiolle.