- 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-Error
och 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-Error
ett 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-Error
uttalanden 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 typenErrorRecord
som 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
ochScriptLineNumber
dä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 intecatch
det., 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-Error
som 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.