- 05/23/2020
- 14 minutter på å lese
-
- j
- f
- s
Feil håndtering er bare en del av livet når det kommer til å skrive kode. Vi kan ofte kontrollere og validateconditions for forventet atferd. Når det uventede skjer, slår vi til unntak håndtering., Youcan enkelt håndtere unntak som er generert av andre mennesker koden eller du kan generere ownexceptions for andre å håndtere.
Merk
Den opprinnelige versjonen av denne artikkelen dukket opp på bloggen skrevet av @KevinMarquette. ThePowerShell team takk Kevin for å dele denne innhold med oss. Sjekk ut bloggen hans atPowerShellExplained.com.
Grunnleggende terminologi
Vi trenger for å dekke noen grunnleggende begreper før vi hoppe inn i dette.
Unntak
Et Unntak er som en hendelse som er opprettet ved normal håndtering av feil ikke kan håndtere problemet.,Prøver å dele et tall med null eller kjører ut av minnet er eksempler på noe som skaper anexception. Noen ganger forfatteren av den koden du bruker skaper unntak for visse issueswhen de skjer.
Kaste og Fange
Når et unntak skjer, sier vi at et unntak er kastet. For å håndtere en kastet unntak, dere trenger for å fange det. Hvis et unntak er kastet, og det er ikke fanget opp av noe, skriptet stopsexecuting.
call stack
call stack er listen over funksjoner som har ringt hverandre., Når en funksjon er kalt, itgets lagt til stakken eller toppen av listen. Når funksjonen avsluttes eller returnerer, det er removedfrom stabelen.
Når et unntak er kastet, som kaller stack er sjekket for et unntak fra behandler til å catchit.
Avslutning, og ikke-terminerende feil
Et unntak er generelt en avslutning feil. En kastet unntak er enten være fanget eller itterminates dagens kjøring. Standard, et ikke-terminerende feil er generert av Write-Error
og det legger en feil til output stream uten å kaste et unntak.,
jeg påpeke dette fordi Write-Error
og andre ikke-terminerende feil ikke utløsecatch
.
Svelge et unntak
Dette er når du fange en feil bare for å undertrykke det. Gjør dette med forsiktighet fordi det kan maketroubleshooting problemer svært vanskelig.
Grunnleggende kommandosyntaksen
Her er en rask oversikt over de grunnleggende unntak håndtering syntaks brukt i PowerShell.
Kaste
for Å lage vår egen unntak hendelse, vi kaste et unntak med throw
søkeord.,
function Start-Something{ throw "Bad thing happened"}
Dette skaper en runtime unntak som er en avslutning feil. Det er håndtert av en catch
i acalling funksjon i eller ut av manus med en melding som dette.
Skrive-Feil -ErrorAction Stoppe
jeg nevnte at Write-Error
ikke kaste en avslutning feil som standard. Hvis du vil angi-ErrorAction Stop
, Write-Error
genererer en avslutning feil som kan bli behandlet med encatch
.,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
Takk til Lee Dailey for at du minner om bruk av -ErrorAction Stop
denne måten.
Cmdlet -ErrorAction Stoppe
Hvis du vil angi -ErrorAction Stop
på en avansert funksjon eller cmdlet, det viser alle Write-Error
uttalelser til avslutning feil som stopper utførelsen eller som kan håndteres av en catch
.,
Start-Something -ErrorAction Stop
Try/Catch
Den måten unntak håndtering fungerer i PowerShell (og mange andre språk) er at du først try
asection av kode, og hvis det kaster en feilmelding, kan du catch
det. Her er et kjapt eksempel.
catch
skriptet kjører bare hvis det er en avslutning feil. Hvis try
utføres på riktig måte, thenit hopper over catch
.,
Prøv/Endelig
noen Ganger trenger du ikke å håndtere en feil, men fortsatt trenger noen kode for å utføre hvis en exceptionhappens eller ikke. En finally
skriptet gjør akkurat det.
Ta en titt på dette eksempelet:
Når du åpner eller koble til en ressurs, bør du lukke den. Hvis ExecuteNonQuery()
throwsan unntak, – tilkoblingen ikke er lukket. Her er den samme koden i en try/finally
blokker.
I dette eksempelet, forbindelsen er lukket, dersom det er en feil. Det også er lukket hvis det er noerror., finally
skriptet kjøres hver gang.
Fordi du er ikke fanger unntak, er det likevel blir dyrket opp samtalen stabelen.
Try/Catch/Endelig
Det er helt gyldig å bruke catch
og finally
sammen. Mesteparten av tiden vil du bruke det ene eller andre, men du kan finne scenarier der du kan bruke begge.
$PSItem
Nå som vi har fått den grunnleggende ute av veien, kan vi grave litt dypere.,
i catch
blokker, det er en automatisk variabel ($PSItem
eller $_
) av type ErrorRecord
som inneholder detaljer om unntak. Her er en rask oversikt over noen av de keyproperties.
For disse eksemplene har jeg brukt en ugyldig bane i ReadAllText
for å generere dette unntaket.
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString()
Dette gir deg den reneste melding til bruk i logging og generell utgang., ToString()
isautomatically kalt hvis $PSItem
er plassert inne i en streng.
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
Denne eiendommen inneholder ekstra informasjon som samles inn av PowerShell om funksjonen eller scriptwhere unntak ble kastet. Her er InvocationInfo
fra prøven unntak av at Icreated.
De viktige detaljene her viser ScriptName
, Line
av koden ScriptLineNumber
hvor bruken i gang.
$PSItem.,ScriptStackTrace
Denne eiendommen viser rekkefølgen av funksjonskall som fikk deg til koden der unntaket wasgenerated.
jeg kan bare ringe til funksjoner i det samme skriptet men dette ville spore samtaler om multiplescripts var involvert.
$PSItem.Unntak
Dette er faktisk unntaket som ble kastet.
$PSItem.Unntak.Melding
Dette er de generelle melding som beskriver de unntak og er et godt utgangspunkt whentroubleshooting. De fleste unntakene har en standard melding, men kan også være satt til noe tilpasset whenthe unntak blir kastet.,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
Dette er også melding tilbake når du ringer $PSItem.ToString()
hvis det var ikke ett sett påErrorRecord
.
$PSItem.Unntak.InnerException
Unntak kan inneholde indre unntak. Dette er ofte tilfellet når koden du callingcatches et unntak, og kaster et annet unntak. Den opprinnelige unntak er plassert insidethe nye unntak.
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
jeg vil tilbake til dette senere når jeg snakker om re-kaste unntak.
$PSItem.Unntak.,StackTrace
Dette er StackTrace
for unntak. Jeg viste en ScriptStackTrace
ovenfor, men denne er forthe samtaler til å forvaltet kode.
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 )
Du får bare denne stack trace når arrangementet er kastet ut forvaltet kode. Jeg kaller en .NETframework funksjon direkte, så det er alt vi kan se i dette eksemplet. Vanligvis når du’relooking på en stack trace, du er på jakt etter hvor koden slutter og systemet samtaler begynne.
Arbeide med unntak
Det er mer unntak enn de grunnleggende syntaks og unntak egenskaper.,
Fange skrevet unntak
Du kan være selektiv med det unntak at du fange. Unntak har type og du kan specifythe type unntak du ønsker å fange.
unntak typen er sjekket for hver catch
blokker før man er funnet som passer dine unntak.Det er viktig å innse at unntak kan arve fra andre unntak. I eksempelet ovenfor,FileNotFoundException
arver fra IOException
. Så hvis IOException
var først, deretter itwould får ringt i stedet., Bare én catch-blokk er påberopt, selv om det er flere kamper.
Hvis vi hadde en System.IO.PathTooLongException
, IOException
ville kampen, men hvis vi hadde enInsufficientMemoryException
så ingenting skulle få det, og det ville spre opp stakken.
Fange flere typer på en gang
Det er mulig å ta flere unntak typer med samme catch
uttalelse.
Takk /u/Sheppard_Ra
for å foreslå dette tillegg.
Kaster skrevet unntak
Du kan kaste skrevet unntak i PowerShell., I stedet for å ringe throw
med en streng:
throw "Could not find: $path"
Bruk et unntak akselerator som dette:
throw "Could not find: $path"
Men du må angi en melding når du gjør det på den måten.
Du kan også opprette en ny instans av et unntak fra å bli kastet. Meldingen er valgfritt når du dothis fordi systemet har standard meldinger for alle innebygde unntak.
throw ::new()throw ::new("Could not find path: $path")
Hvis du ikke bruker PowerShell 5.0 eller høyere, må du bruke eldre New-Object
tilnærming.,
Ved hjelp av et maskinskrevet unntak, kan du (eller andre) kan fange unntak av den type som er nevnt i theprevious delen.
Skrive-Feil -Unntak
Vi kan legge til disse skrev unntak til Write-Error
og vi kan fortsatt catch
feil ved exceptiontype. Bruk Write-Error
som i disse eksemplene:
Så vi kan ta det som dette:
catch { Write-Log $PSItem.ToString()}
Den store listen over .NET unntak
jeg samlet en master liste med hjelp av Reddit/r/PowerShell samfunnet som containshundreds av .,NET unntak for å utfylle dette innlegget.
- Den store listen over .NET unntak
jeg starter ved å søke på at listen for unntak som føler at de ville passe godt for mysituation. Du bør prøve å bruke unntakene i basen System
navnerom.
Unntak er objekter
Hvis du begynner å bruke mye av skrevet unntak, husk at de er objekter. Ulike exceptionshave forskjellige konstruktører og egenskaper., Hvis vi ser på FileNotFoundExceptiondocumentation for System.IO.FileNotFoundException
, ser vi at vi kan passere i en melding og en filepath.
::new("Could not find file", $path)
Og det har en FileName
eiendom som avslører at filbanen.
catch { Write-Output $PSItem.Exception.FileName}
Du bør konsultere .NET dokumentasjon som for andre konstruktører og objekt egenskaper.
Re-kaster et unntak
Hvis alt du kommer til å gjøre i catch
blokker er throw
samme unntak, så ikke catch
det., Du bør bare catch
et unntak som du har tenkt å håndtere eller utføre noen handling når ithappens.
Det er tider hvor du ønsker å utføre en handling på et unntak, men re-kast unntak sosomething nedstrøms kan håndtere det. Vi kan skrive en melding eller logg problemet i nærheten av der wediscover det, men håndterer problemet lenger opp i stabelen.
catch{ Write-Log $PSItem.ToString() throw $PSItem}
det er Interessant nok, vi kan kalle throw
fra catch
og det re-kaster currentexception.,
catch{ Write-Log $PSItem.ToString() throw}
Vi ønsker å re-kast unntak for å bevare den opprinnelige utførelse informasjon som kilde scriptand linje nummer. Hvis vi kaster et nytt unntak på dette punktet, det skjuler der unntaket i gang.
Re-lanserer en ny unntak
Hvis du fange et unntak, men du vil kaste en annen en, så bør du nest originalexception inne i det nye. Dette gjør at noen ned stabelen for å få tilgang til det som$PSItem.Exception.InnerException
.
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$PSCmdlet.,ThrowTerminatingError()
en ting som jeg ikke liker om bruk av throw
for raw unntak er at feilen messagepoints på throw
uttalelse, og angir at linjen er der problemet er.
etter å Ha feilmeldingen forteller meg at min skriptet er ødelagt fordi jeg kalt throw
på linje 31 er abad melding til brukere av skriptet for å se. Det trenger ikke fortelle dem noe nyttig.
Dexter Dhami påpekt at jeg kan bruke ThrowTerminatingError()
for å korrigere det.,
Hvis vi antar at ThrowTerminatingError()
ble kalt inne i en funksjon kalt Get-Resource
, thenthis er feil at vi ville se.
vil du se hvordan det poeng til Get-Resource
funksjon som er kilden til problemet? Som forteller theuser noe nyttig.
Fordi $PSItem
er en ErrorRecord
, kan vi også bruke ThrowTerminatingError
denne måten å re-kast.,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
Dette endrer kilde til feil å Cmdleten og skjule det innvendige av ditt arrangement suge brukere av Cmdleten.
Prøv kan opprette avslutte feil
Kirk Munro peker på at noen unntak er det bare å avslutte feil når den utføres inne i entry/catch
blokker. Her er det eksempel han ga meg som genererer en dividere med null runtime unntak.
function Start-Something { 1/(1-1) }
Deretter starter det som dette for å se det generere feil og fortsatt utgang meldingen.,
&{ Start-Something; Write-Output "We did it. Send Email" }
Men ved å plassere den samme koden i en try/catch
, ser vi at noe annet skjer.
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
Vi se feilen bli en avslutning feil og ikke sende ut den første meldingen. Det jeg ikke likeabout denne er at du kan ha denne koden i en funksjon, og det fungerer annerledes hvis someoneis ved hjelp av en try/catch
.
jeg har ikke løp inn problemer med dette selv, men det er hjørne tilfelle å være klar over.
$PSCmdlet.,ThrowTerminatingError() inne i try/catch
En nyanse av $PSCmdlet.ThrowTerminatingError()
er at det skaper en avslutning feil i yourCmdlet men det blir til en ikke-terminerende feil etter at den forlater Cmdleten. Dette etterlater burdenon anroper-av-funksjon for å bestemme hvordan de skal håndtere feil. De kan slå den tilbake i aterminating feil ved å bruke -ErrorAction Stop
eller kall det fra innenfor en try{...}catch{...}
.,
Offentlig funksjon maler
En siste ta en måte jeg hadde med min samtale med Kirk Munro var at han plasserer entry{...}catch{...}
rundt hver begin
, process
og end
blokker i alle hans advancedfunctions. I de generiske catch-blokker, han har en enkelt linje med$PSCmdlet.ThrowTerminatingError($PSItem)
for å håndtere alle unntak forlate sine funksjoner.
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
Fordi alt er i en try
uttalelse innen sine funksjoner, alt fungerer konsekvent., Thisalso gir rene feil til sluttbruker som skjuler den interne koden fra den genererte feil.
Felle
jeg fokuserte på try/catch
aspekt av unntak. Men det er en arv har jeg trenger å mentionbefore vi pakker opp dette.
En trap
er plassert i et script eller funksjon å fange alle unntak som skjer i det omfang. Whenan unntak skjer, koden i trap
utføres, og deretter normal kode fortsetter. Ifmultiple unntak skje, så den fellen er kalt over og over.,
jeg personlig aldri vedtatt denne tilnærmingen, men jeg kan se verdien i admin eller controller skript thatlog alle unntak, så fortsette å kjøre.
avslutning
å Legge til rette unntaksbehandling å lesa ikke bare gjøre dem mer stabil, men også gjør iteasier for deg å feilsøke disse unntakene.
jeg brukte mye tid på å snakke throw
fordi det er et sentralt begrep når vi snakker om exceptionhandling., PowerShell også ga oss Write-Error
som håndterer alle situasjoner hvor du ville brukethrow
. Så tror ikke at du trenger å bruke throw
etter å ha lest dette.
Nå som jeg har tatt deg tid til å skrive om unntak håndtering i denne detalj, kommer jeg til toswitch over til å bruke Write-Error -Stop
for å generere feil i koden min. Jeg er også tenkt å takeKirk råd og foreta ThrowTerminatingError
min goto unntak handler for hver funksjon.