Welcome to Our Website

Alt, hvad du ønskede at vide om undtagelser

  • 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-Errorog 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-Errorgenererer 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-Errorerklæ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 catchog 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 ScriptLineNumberhvor 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-Errorog 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 catchdet., 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.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *