- 05/23/2020
- 14 minutter på at læse
-
- j
- f
- s
fejlhåndtering er bare en del af livet, når det kommer til at skrive kode. Vi kan ofte kontrollere og validerebetingelser for forventet adfærd. Når det uventede sker, vender vi os til undtagelseshåndtering., Youcan nemt håndtere undtagelser genereret af andre folks kode, eller du kan generere dine o .necepceptions for andre at håndtere.
Bemærk
den originale version af denne artikel blev vist på bloggen skrevet af @Kevinmar .uette. Thepo .ershell team tak Kevin for at dele dette indhold med os. Tjek venligst hans blog atPowerShellExplained.com.
grundlæggende terminologi
Vi er nødt til at dække nogle grundlæggende udtryk, før vi hopper ind i denne.
undtagelse
en undtagelse er som en begivenhed, der oprettes, når normal fejlhåndtering ikke kan håndtere problemet.,Forsøger at opdele et tal med nul eller løbe tør for hukommelse er eksempler på noget, der skaber Ane .ception. Nogle gange skaber forfatteren af den kode, du bruger, undtagelser for visse problemernår de sker.
kast og Fang
Når en undtagelse sker, siger vi, at en undtagelse kastes. For at håndtere en kastet undtagelse, duskal fange den. Hvis en undtagelse kastes, og den ikke fanges af noget, stopper scriptetudførelse.
opkaldsstakken
opkaldsstakken er listen over funktioner, der har kaldt hinanden., Når en funktion kaldes, er denbliver tilføjet til stakken eller toppen af listen. Når funktionen går ud eller vender tilbage, fjernes denfra stakken.
Når en undtagelse kastes, kontrolleres denne opkaldsstabel for at en undtagelseshåndterer kan fange.
afslutnings-og ikke-afslutningsfejl
en undtagelse er generelt en afslutningsfejl. En kastet undtagelse er enten fanget eller denafslutter den aktuelle udførelse. Som standard genereres en ikke-afslutningsfejl af Write-Error
, og den tilføjer en fejl til outputstrømmen uden at kaste en undtagelse.,
jeg påpeger dette, fordi Write-Error
og andre ikke-terminerende fejl ikke udløsercatch
.
slukning af en undtagelse
Dette er, når du får en fejl bare for at undertrykke den. Gør dette med forsigtighed, fordi det kan gørefejlfinding spørgsmål meget vanskeligt.
grundlæggende kommandosynta.
Her er et hurtigt overblik over den grundlæggende undtagelseshåndteringssynta., der bruges i Po .ershell.
kast
for at oprette vores egen undtagelsesbegivenhed kaster vi en undtagelse medthrow
nøgleord.,
function Start-Something{ throw "Bad thing happened"}
dette skaber en runtime undtagelse, der er en afslutningsfejl. Det håndteres af en catch
i signalfunktion eller afslutter scriptet med en meddelelse som denne.
Writerite-Error-ErrorAction Stop
jeg nævnte, atWrite-Error
ikke kaster en afslutningsfejl som standard. Hvis du angiver-ErrorAction Stop
Write-Error
genererer en afslutning fejl, der kan håndteres med encatch
.,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
Tak til Lee Dailey for at minde om brug af -ErrorAction Stop
denne måde.
Cmdlet -ErrorAction Stop
Hvis du angiver -ErrorAction Stop
på ethvert avanceret funktion eller cmdlet, det viser alle Write-Error
erklæringer til afslutning af fejl, der stopper udførelse, eller som kan håndteres af en catch
.,
Start-Something -ErrorAction Stop
Try/Catch
Den måde, exception handling virker i PowerShell (og mange andre sprog) er, at du først try
enafsnittet af kode, og hvis det kaster en fejl, kan du catch
det. Her er en hurtig prøve.
catch
scriptet kører kun, hvis der er en afslutningsfejl. Hvis try
udføres korrekt, springer den over catch
.,
prøv/endelig
Nogle gange behøver du ikke at håndtere en fejl, men har stadig brug for en kode for at udføre, hvis en e .ceptionhappens eller ej. A finally
script gør netop det.
Tag et kig på dette eksempel:
hver gang du åbner eller opretter forbindelse til en ressource, skal du lukke den. HvisExecuteNonQuery()
kaster en undtagelse, er forbindelsen ikke lukket. Her er den samme kode inde i en try/finally
blok.
i dette eksempel lukkes forbindelsen, hvis der er en fejl. Det er også lukket, hvis der er noerror., Scriptetfinally
kører hver gang.
fordi du ikke fanger undtagelsen, bliver den stadig udbredt op i opkaldsstakken.
prøv/Catch/Finally
det er helt gyldigt at bruge catch
og finally
sammen. Det meste af tiden bruger du den ene eller den anden, men du kan finde scenarier, hvor du bruger begge dele.
$PSItem
nu hvor vi fik det grundlæggende ud af vejen, kan vi grave lidt dybere.,
i catch
blok, der er en automatisk variabel ($PSItem
eller $_
) af typen ErrorRecord
, der indeholder oplysninger om undtagelse. Her er et hurtigt overblik over nogle af tasternes egenskaber.
for disse eksempler brugte jeg en Ugyldig sti i ReadAllText
for at generere denne undtagelse.
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString ()
Dette giver dig den reneste besked til brug i logning og generel output., ToString()
isautomatisk kaldet hvis $PSItem
er placeret inde i en streng.
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
denne egenskab indeholder yderligere oplysninger indsamlet af Po .ershell om funktionen eller scriptwherehere undtagelsen blev kastet. Her er InvocationInfo
fra prøven undtagelse, Icreated.
De vigtige detaljer her viser ScriptName
Line
kode og ScriptLineNumber
hvor aktiveringen i gang.
$PSItem.,ScriptStackTrace
denne egenskab viser rækkefølgen af funktionskald, der fik dig til koden, hvor undtagelsen var genereret.
jeg foretager kun opkald til funktioner i det samme script, men dette ville spore opkaldene, hvis multiplescripts var involveret.
$PSItem.Undtagelse
Dette er den faktiske undtagelse, der blev kastet.
$PSItem.Undtagelse.Besked
Dette er den generelle meddelelse, der beskriver undtagelsen og er et godt udgangspunkt, nårfejlfinding. De fleste undtagelser har en standardmeddelelse, men kan også indstilles til noget brugerdefineret, når undtagelsen kastes.,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
det er også det budskab tilbage, når du ringer $PSItem.ToString()
hvis der ikke var et sæt påErrorRecord
.
$PSItem.Undtagelse.Innereeptionception
undtagelser kan indeholde indre undtagelser. Dette er ofte tilfældet, når koden du ringerfanger en undtagelse og kaster en anden undtagelse. Den oprindelige undtagelse er placeret indeniden nye undtagelse.
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
Jeg vil revidere dette senere, når jeg taler om at kaste undtagelser igen.
$PSItem.Undtagelse.,StackTrace
Dette er StackTrace
for undtagelsen. Jeg viste en ScriptStackTrace
ovenfor, men denne er foropkald til administreret 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 kun denne stak spor, når begivenheden er kastet fra administreret kode. Jeg ringer til en .Netframe .ork fungerer direkte, så det er alt, hvad vi kan se i dette eksempel. Generelt når du kigger på et stakspor, Leder du efter, hvor din kode stopper, og systemkaldene begynder.
arbejde med undtagelser
Der er mere undtagelser end de grundlæggende syntaks og undtagelsesegenskaber.,
Catching typed e exceptionsceptions
Du kan være selektiv med de undtagelser, du fanger. Undtagelser har en type, og du kan angiveden type undtagelse, du vil fange.
bortset type kontrolleres for hvert catch
bloker indtil der findes en, der passer til dine undtagelse.Det er vigtigt at indse, at undtagelser kan arve fra andre undtagelser. I eksemplet ovenfor arverFileNotFoundException
fra IOException
. Så hvis IOException
var først, så ville det blive kaldt i stedet., Kun en fangstblok påberåbes, selvom der er flere kampe.
Hvis vi havde en System.IO.PathTooLongException
IOException
vil kamp, men hvis vi havde enInsufficientMemoryException
så intet ville fange det, og det ville forplante sig op i stakken.
Fang flere typer på gang
det er muligt at fange flere undtagelsestyper med det sammecatch
erklæring.
Tak /u/Sheppard_Ra
for at foreslå denne tilføjelse.
kaste indtastede undtagelser
Du kan kaste indtastede undtagelser i Po .ershell., I stedet for at kalde throw
med en streng:
throw "Could not find: $path"
Brug en undtagelse accelerator som dette:
throw "Could not find: $path"
Men du er nødt til at angive en meddelelse, når du gør det på den måde.
Du kan også oprette en ny forekomst af en undtagelse, der skal kastes. Meddelelsen er valgfri, når du gør dette, fordi systemet har standardmeddelelser for alle indbyggede undtagelser.
throw ::new()throw ::new("Could not find path: $path")
Hvis du ikke bruger PowerShell 5.0 eller højere, skal du bruge den ældre New-Object
tilgang.,
ved at bruge en indtastet undtagelse kan du (eller andre) fange undtagelsen efter typen som nævnt i theprevious sektion.
skrive-fejl-undtagelse
Vi kan tilføje disse indtastede undtagelser til Write-Error
og vi kan stadig catch
fejlene efter e .ceptiontype. Brug Write-Error
som i disse eksempler:
Så vi kan fange det, som dette:
catch { Write-Log $PSItem.ToString()}
Den store liste af .NET undtagelser
jeg er udarbejdet en overordnet liste med hjælp af Reddit/r/PowerShell samfund, der containshundreds af .,Netto undtagelser for at supplere dette indlæg.
- den store liste over.Net undtagelser
Jeg starter med at søge denne liste for undtagelser, der føles som om de ville være en god pasform til mysituation. Du bør prøve at bruge undtagelser i basen System
navneområde.
undtagelser er objekter
Hvis du begynder at bruge mange indtastede undtagelser, skal du huske, at de er objekter. Forskellige undtagelserhar forskellige konstruktører og egenskaber., Hvis vi ser på Filenotfounde .ceptiondocumentation for System.IO.FileNotFoundException
, ser vi, at vi kan videregive en meddelelse og en filsti.
::new("Could not find file", $path)
og det har en FileName
egenskab, der udsætter den filsti.
catch { Write-Output $PSItem.Exception.FileName}
Du bør læse.net-dokumentationen for andre konstruktører og objektegenskaber.
Re-kaste en exception
Hvis du kommer til at gøre i din catch
bloker er throw
samme undtagelse, så skal du ikke catch
det., Du bør kun catch
en undtagelse, som du planlægger at håndtere eller udføre nogle handlinger, når detsker.
Der er tidspunkter, hvor du vil udføre en handling på en undtagelse, men kast undtagelsen igen sånoget nedstrøms kan håndtere det. Vi kunne skrive en besked eller logge problemet tæt på hvoriscediscover det, men håndtere problemet længere op stakken.
catch{ Write-Log $PSItem.ToString() throw $PSItem}
det er Interessant nok, vi kan kalde throw
fra catch
og det re-kaster currentexception.,
catch{ Write-Log $PSItem.ToString() throw}
Vi ønsker at kaste undtagelsen igen for at bevare de originale eksekveringsoplysninger som kilde scriptand linjenummer. Hvis vi kaster en ny undtagelse på dette tidspunkt, skjuler den, hvor undtagelsen startede.
genudkast en ny undtagelse
Hvis du fanger en undtagelse, men du vil smide en anden, skal du indlejre originaleeptionception inde i den nye. Dette gør det muligt for nogen ned i stakken at få adgang til den som$PSItem.Exception.InnerException
.
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$PSCmdlet.,ThrowTerminatingError()
en ting, som jeg ikke bryder mig om ved hjælp af throw
for rå undtagelser er, at den fejl, messagepoints på throw
erklæring, og som angiver, at linjen er, hvor problemet er.
under fejlmeddelelsen fortælle mig, at mit script er brudt, fordi jeg kaldte throw
on line 31 er abad besked for brugere af dit script til at se. Det fortæller dem ikke noget nyttigt.
de .ter Dhami påpegede, at jeg kan bruge ThrowTerminatingError()
for at rette op på det.,
Hvis vi antager, at ThrowTerminatingError()
blev kaldt inde i en funktion kaldetGet-Resource
, såDette er den fejl, vi ville se.
kan du se, hvordan det peger på Get-Resource
funktionen som kilde til problemet? Det fortæller brugeren noget nyttigt.
Fordi $PSItem
er et ErrorRecord
, kan vi også bruge ThrowTerminatingError
denne måde at re-kast.,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
dette ændrer fejlkilden til Cmdlet og skjuler interne funktioner fraBrugerne af din Cmdlet.
Try kan oprette afslutningsfejl
Kirk Munro påpeger, at nogle undtagelser kun er afslutningsfejl, når de udføres i entry/catch
blok. Her er det eksempel, han gav mig, der genererer en kløft med nul runtime undtagelse.
function Start-Something { 1/(1-1) }
derefter påberåbe sig det sådan for at se det generere fejlen og stadig udsende meddelelsen.,
&{ Start-Something; Write-Output "We did it. Send Email" }
Men ved at placere den samme kode inde i en try/catch
, ser vi noget andet til at ske.
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
Vi ser fejlen blive en afslutningsfejl og udsender ikke den første meddelelse. Hvad jeg ikke kan lideom denne er, at du kan have denne kode i en funktion, og den virker anderledes, hvis nogen bruger en try/catch
.
Jeg har ikke kørt ind i problemer med dette selv, men det er hjørnesagen at være opmærksom på.
$pscmdlet.,ThrowTerminatingError() inde i try/catch
En nuance af $PSCmdlet.ThrowTerminatingError()
er, at det skaber en afslutning fejl i yourCmdlet men det bliver til en ikke-afslutning fejl, når det forlader din Cmdlet. Dette efterlader byrdenpå opkalderen af din funktion for at bestemme, hvordan du skal håndtere fejlen. De kan vende det tilbage til atterminere fejl ved at bruge -ErrorAction Stop
eller kalde det inde fra en try{...}catch{...}
.,
Offentlig funktion skabeloner
En sidste træffe en måde, som jeg havde med min samtale med Kirk Munro var, at han lægger entry{...}catch{...}
omkring hvert begin
process
og end
bloker i al sin advancedfunctions. I disse generiske fangstblokke har han en enkelt linje ved hjælp af$PSCmdlet.ThrowTerminatingError($PSItem)
for at håndtere alle undtagelser, der forlader hans funktioner.
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
Fordi alt er i en try
erklæring inden for sit hverv, alt fungerer konsekvent., Dettegiver også rene fejl til slutbrugeren, der skjuler den interne kode fra den genererede fejl.
Trap
Jeg fokuserede påtry/catch
aspekt af undtagelser. Men der er en arv funktion, jeg skal nævne, før vi afslutter dette.
A trap
er placeret i et script eller en funktion for at fange alle undtagelser, der sker i dette omfang. Nåren undtagelse sker, koden i trap
udføres, og derefter fortsætter den normale kode. Hvisflere undtagelser sker, så kaldes fælden igen og igen.,
jeg personligt aldrig vedtaget denne tilgang, men jeg kan se værdien i admin eller controller scripts thatlog alle undtagelser, så stadig fortsætte med at udføre.
afsluttende bemærkninger
tilføjelse af korrekt undtagelseshåndtering til dine scripts gør dem ikke kun mere stabile, men gør det også lettere for dig at fejlfinde disse undtagelser.
Jeg brugte meget tid på at tale throw
fordi det er et kernekoncept, når man taler om e .ceptionhandling., PowerShell også gav os Write-Error
, der håndterer alle de situationer hvor du ville brugethrow
. Så tror ikke, at du skal bruge throw
efter at have læst dette.
nu hvor jeg har taget mig tid til at skrive om undtagelseshåndtering i denne detalje, skal jeg skifte til at bruge Write-Error -Stop
for at generere fejl i min kode. Jeg vil også takekirks råd og lave ThrowTerminatingError
min Goto-undtagelseshandler for hver funktion.