Welcome to Our Website

allt du ville veta om undantag

  • 05/23/2020
  • 14 minuter att läsa
    • j
    • f
    • s

felhantering är bara en del av livet när det gäller att skriva kod. Vi kan ofta kontrollera och valideravillkor för förväntat beteende. När det oväntade händer vänder vi oss till undantagshantering., Du kan enkelt hantera undantag som genereras av andras kod eller så kan du skapa din egenundantag för andra att hantera.

Obs

den ursprungliga versionen av denna artikel dök upp på bloggen skriven av @KevinMarquette. ThePowerShell team tack Kevin för att dela innehållet med oss. Vänligen kolla in hans blogg atPowerShellExplained.com.

grundläggande terminologi

Vi måste täcka några grundläggande termer innan vi hoppar in i den här.

undantag

ett undantag är som en händelse som skapas när normal felhantering inte kan hantera problemet.,Att försöka dela ett tal med noll eller slut på minne är exempel på något som skapar anexception. Ibland skapar författaren till koden du använder undantag för vissa frågornär de händer.

kasta och fånga

När ett undantag händer säger vi att ett undantag kastas. För att hantera ett kastat undantag, dubehöver fånga det. Om ett undantag kastas och det inte fångas av något, slutar skriptetsexecuting.

samtalsstacken

samtalsstacken är listan över funktioner som har kallat varandra., När en funktion kallas, är detfår läggas till stapeln eller toppen av listan. När funktionen lämnar eller returnerar tas den bortfrån stapeln.

När ett undantag kastas, kontrolleras den samtalsstacken för att en undantagshanterare ska kunna fånga upp det.

avslutande och icke-avslutande fel

ett undantag är i allmänhet ett avslutande fel. Ett kastat undantag är antingen fångas eller detterminerar det aktuella utförandet. Som standard genereras ett icke-avslutande fel av Write-Error och det lägger till ett fel i utmatningsströmmen utan att kasta ett undantag.,

jag påpekar detta eftersom Write-Erroroch andra icke-avslutande fel inte utlösercatch.

svälja ett undantag

detta är när du fångar ett fel bara för att undertrycka det. Gör detta med försiktighet eftersom det kan maketroubleshooting frågor mycket svårt.

grundläggande kommandosyntax

här är en snabb översikt över den grundläggande undantagshanteringssyntax som används i PowerShell.

kasta

för att skapa vår egen undantagshändelse kastar vi ett undantag med nyckelordetthrow.,

function Start-Something{ throw "Bad thing happened"}

detta skapar ett runtime undantag som är ett avslutande fel. Den hanteras av encatch I acaling-funktionen eller avslutar skriptet med ett meddelande som detta.

skrivfel-ErrorAction Stop

jag nämnde att Write-Error inte kastar ett avslutande fel som standard. Om du anger-ErrorAction Stop genererar Write-Errorett avslutande fel som kan hanteras med ettcatch.,

Write-Error -Message "Houston, we have a problem." -ErrorAction Stop

Tack till Lee Dailey för att påminna om att använda-ErrorAction Stop på det här sättet.

Cmdlet-ErrorAction Stop

om du anger-ErrorAction Stop på en avancerad funktion eller cmdlet, förvandlar det allaWrite-Erroruttalanden till avslutande fel som stoppar utförandet eller som kan hanteras av encatch.,

Start-Something -ErrorAction Stop

försök/fånga

sättet undantagshantering fungerar i PowerShell (och många andra språk) är att du försttry asektion av kod och om det kastar ett fel kan ducatch det. Här är ett snabbt prov.

catch script körs endast om det finns ett avslutande fel. Omtry körs korrekt hoppar den övercatch.,

försök/slutligen

Ibland behöver du inte hantera ett fel men behöver fortfarande en kod för att köra om en exceptionhappens eller inte. Afinally script gör exakt det.

ta en titt på det här exemplet:

När du öppnar eller ansluter till en resurs bör du stänga den. Om undantagetExecuteNonQuery() throwsan stängs inte anslutningen. Här är samma kod i ett try/finally – block.

i det här exemplet stängs anslutningen om det finns ett fel. Det är också stängt om det inte finns någonfel., Skriptetfinally körs varje gång.

eftersom du inte fångar undantaget blir det fortfarande förökat upp samtalsstacken.

försök/fånga/slutligen

det är helt giltigt att användacatch ochfinally tillsammans. För det mesta använder du den ena ellerDen andra, men du kan hitta scenarier där du använder båda.

$psitem

Nu när vi fick grunderna ur vägen kan vi gräva lite djupare.,

inuticatch – blocket finns en automatisk variabel ($PSItem eller$_) av typenErrorRecordsom innehåller detaljer om undantaget. Här är en snabb översikt över några av knappenegenskaper.

för dessa exempel använde jag en Ogiltig sökväg i ReadAllText för att generera detta undantag.

::ReadAllText( '\\test\no\filefound.log')

PSItem.ToString ()

detta ger dig det renaste meddelandet att använda i loggning och allmän utmatning., ToString() kallas automatiskt om$PSItem placeras inuti en sträng.

catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}

$psitem.InvocationInfo

den Här egenskapen innehåller ytterligare information som samlas in av PowerShell om funktion eller scriptwhere undantag kastas. Här är InvocationInfo från exempelundantaget som Icreated.

viktiga detaljer här visarScriptName, kodenLine ochScriptLineNumberdär Anropet startade.

$psitem.,ScriptStackTrace

den här egenskapen visar ordningen för funktionsanrop som fick dig till koden där undantaget genererades.

Jag ringer bara till funktioner i samma skript men det skulle spåra samtalen om multiplescripts var inblandade.

$psitem.Undantag

detta är det faktiska undantaget som kastades.

$psitem.Undantag.Meddelande

det här är det allmänna meddelandet som beskriver undantaget och är en bra utgångspunkt när det gäller inköp. De flesta undantag har ett standardmeddelande men kan också ställas in på något anpassat närundantaget kastas.,

PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."

det här är också meddelandet som returneras när du ringer $PSItem.ToString() om det inte fanns någon uppsättning påErrorRecord.

$psitem.Undantag.InnerException

undantag kan innehålla inre undantag. Detta är ofta fallet när koden du kallarfår ett undantag och kastar ett annat undantag. Det ursprungliga undantaget placeras inutiDet nya undantaget.

PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.

Jag kommer att se över detta senare när jag pratar om att kasta undantag igen.

$psitem.Undantag.,StackTrace

detta är StackTrace för undantaget. Jag visade enScriptStackTrace ovan, men den här är föranrop till hanterad kod.

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 bara detta stackspår när händelsen kastas från hanterad kod. Jag ringer a .NETframework fungerar direkt så det är allt vi kan se i det här exemplet. Generellt när du ’relooking på en stack spår, du’ re letar efter där din kod stannar och systemanrop börjar.

arbeta med undantag

det finns mer undantag än de grundläggande syntax-och undantagsegenskaperna.,

fånga maskinskrivna undantag

Du kan vara selektiv med de undantag som du fångar. Undantag har en typ och du kan angeden typ av undantag du vill fånga.

undantagstypen är markerad för varjecatch block tills en hittas som matchar din exception.It det är viktigt att inse att undantag kan ärva från andra undantag. I exemplet ovan ärverFileNotFoundException från IOException. Så om IOException var först, skulle det bli kallat istället., Endast ett fångstblock åberopas även om det finns flera matcher.

om vi hade en System.IO.PathTooLongException, skulle IOException matcha men om vi hade enInsufficientMemoryException då skulle ingenting fånga det och det skulle sprida upp stapeln.

fånga flera typer samtidigt

det är möjligt att fånga flera undantagstyper med sammacatch uttalande.

Tack/u/Sheppard_Ra för att föreslå detta tillägg.

kasta maskinskrivna undantag

Du kan kasta maskinskrivna undantag i PowerShell., Istället för att ringa throw med en sträng:

throw "Could not find: $path"

använd en undantagsaccelerator så här:

throw "Could not find: $path"

men du måste ange ett meddelande när du gör det på det sättet.

Du kan också skapa en ny instans av ett undantag som ska kastas. Meddelandet är valfritt när du gör detDet beror på att systemet har standardmeddelanden för alla inbyggda undantag.

throw ::new()throw ::new("Could not find path: $path")

om du inte använder PowerShell 5.0 eller senare måste du använda den äldreNew-Object – metoden.,

genom att använda ett angivet undantag kan du (eller andra) fånga undantaget med den typ som nämns i föregående avsnitt.

skrivfel-undantag

vi kan lägga till dessa typade undantag tillWrite-Error och vi kan fortfarandecatch felen genom exceptiontype. Använd Write-Error som i dessa exempel:

då kan vi fånga det så här:

catch { Write-Log $PSItem.ToString()}

den stora listan med .Net-undantag

jag sammanställde en huvudlista med hjälp av Reddit/r / PowerShell-gemenskapen som innehållershundreds av.,Netto undantag för att komplettera detta inlägg.

  • den stora listan över.Net undantag

Jag börjar med att söka listan efter undantag som känns som om de skulle vara en bra passform för mysituation. Du bör försöka använda undantag i basenSystem namnområde.

undantag är objekt

om du börjar använda många maskinskrivna undantag, kom ihåg att de är objekt. Olika exceptionerhar olika konstruktörer och egenskaper., Om vi tittar på FileNotFoundExceptiondocumentation för System.IO.FileNotFoundException ser vi att vi kan skicka ett meddelande och en filepat.

::new("Could not find file", $path)

och den har en FileName egenskap som exponerar den sökvägen.

catch { Write-Output $PSItem.Exception.FileName}

Du bör konsultera.Net-dokumentationen för andra konstruktörer och objektegenskaper.

kasta om ett undantag

om allt du ska göra i dittcatch-block ärthrow samma undantag, gör intecatchdet., Du bör baracatch ett undantag som du planerar att hantera eller utföra någon åtgärd när ithappens.

det finns tillfällen då du vill utföra en åtgärd på ett undantag men kasta om undantaget sosomething nedströms kan hantera det. Vi kan skriva ett meddelande eller logga problemet nära där wediscover det men hantera problemet längre upp i stapeln.

catch{ Write-Log $PSItem.ToString() throw $PSItem}

intressant nog, vi kan ringathrow inifråncatch och det åter kastar currentexception.,

catch{ Write-Log $PSItem.ToString() throw}

vi vill kasta om undantaget för att bevara den ursprungliga exekveringsinformationen som källskript och radnummer. Om vi kastar ett nytt undantag vid denna tidpunkt döljer det var undantaget började.

omkasta ett nytt undantag

om du får ett undantag men vill kasta ett annat, bör du kapsla originalexception inuti det nya. Detta gör det möjligt för någon att komma åt den som $PSItem.Exception.InnerException.

catch{ throw ::new('Could not access field',$PSItem.Exception)}

$pscmdlet.,ThrowTerminatingError ()

det enda jag inte tycker om att användathrow för råa undantag är att felet messagepoints vidthrow – uttalandet och indikerar att raden är där problemet är.

att ha felmeddelandet berätta för mig att mitt skript är trasigt eftersom jag ringde throw på rad 31 är abad-meddelande för användare av ditt skript att se. Det säger inget användbart.

Dexter Dhami påpekade att jag kan använda ThrowTerminatingError() för att korrigera det.,

om vi antar att ThrowTerminatingError() anropades inuti en funktion som heter Get-Resource, dådet här är det fel som vi skulle se.

ser du hur det pekar på funktionenGet-Resource som källan till problemet? Det säger användaren något användbart.

eftersom$PSItem är enErrorRecord, kan vi också användaThrowTerminatingError detta sätt att kasta om.,

catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}

detta ändrar källan till felet till Cmdlet och döljer de inre delarna av din funktion frånanvändarna på din Cmdlet.

försök kan skapa avslutande fel

Kirk Munro påpekar att vissa undantag endast avslutar fel när de körs i etttry/catch – block. Här är exemplet han gav mig som genererar en klyfta med noll runtime undantag.

function Start-Something { 1/(1-1) }

anropa det så här för att se det generera felet och fortfarande mata ut meddelandet.,

&{ Start-Something; Write-Output "We did it. Send Email" }

men genom att placera samma kod i en try/catch ser vi något annat hända.

try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}

Vi ser att felet blir ett avslutande fel och inte matar ut det första meddelandet. Vad jag inte gillarom den här är att du kan ha den här koden i en funktion och den fungerar annorlunda om någon använder en try/catch.

Jag har inte stött på problem med detta själv men det är hörnfall att vara medveten om.

$pscmdlet.,ThrowTerminatingError() inuti try/catch

En nyans av $PSCmdlet.ThrowTerminatingError() är att det skapar en avslutande fel inom yourCmdlet men det blir till en icke-avslutande fel efter det lämnar din Cmdlet. Detta lämnar belastningenpå uppringaren av din funktion för att bestämma hur man hanterar felet. De kan göra det tillbaka till aterminating error genom att använda -ErrorAction Stop eller ringa det inifrån en try{...}catch{...}.,

offentliga funktionsmallar

en sista ta ett sätt jag hade med mitt samtal med Kirk Munro var att han placerar entry{...}catch{...} runt varjebegin,process ochend blockera i alla hans avanceradefunktioner. I de generiska fångstblocken har han en enda rad med$PSCmdlet.ThrowTerminatingError($PSItem) för att hantera alla undantag som lämnar sina funktioner.

function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}

eftersom allt är i etttry – uttalande inom hans funktioner fungerar allt konsekvent., Detta ger också rena fel till slutanvändaren som döljer den interna koden från det genererade felet.

Trap

Jag fokuserade påtry/catch aspekten av undantag. Men det finns ett arv som jag måste nämna innan vi avslutar det här.

atrap placeras i ett skript eller en funktion för att fånga alla undantag som sker i det tillämpningsområdet. Närett undantag händer körs koden i trap och sedan fortsätter den normala koden. Omflera undantag händer, kallas fällan om och om igen.,

jag personligen aldrig antagit detta tillvägagångssätt men jag kan se värdet i admin eller controller skript somlog alla undantag, sedan fortfarande fortsätta att utföra.

avslutande anmärkningar

lägga till korrekt undantagshantering i dina skript inte bara göra dem mer stabila, men också gör det lättare för dig att felsöka dessa undantag.

Jag spenderade mycket tid på att prata throw eftersom det är ett kärnkoncept när man pratar om exceptionhandling., PowerShell gav oss också Write-Errorsom hanterar alla situationer där du skulle användathrow. Så tro inte att du behöver använda throw efter att ha läst detta.

Nu när jag har tagit mig tid att skriva om undantagshantering i denna detalj, kommer jag att gå över till att använda Write-Error -Stop för att generera fel i min kod. Jag kommer också att takekirks råd och göra ThrowTerminatingError min Goto exception handler för varje funktion.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *