- 05/23/2020
- 14 minuten te lezen
-
- j
- f
- s
Fout afhandeling wordt slechts een deel van het leven als het gaat om het schrijven van code. We kunnen vaak controleren en validatievoorwaarden Voor verwacht gedrag. Wanneer het onverwachte gebeurt, wenden we ons tot de behandeling van uitzonderingen., U kunt gemakkelijk uitzonderingen die worden gegenereerd door de code van anderen verwerken of u kunt uw eigen uitzonderingen genereren voor anderen om te verwerken.
Note
De originele versie van dit artikel verscheen op de blog geschreven door @KevinMarquette. Het PowerShell team bedankt Kevin voor het delen van deze content met ons. Bekijk zijn blog atPowerShellExplained.com.
basisterminologie
We moeten enkele basistermen behandelen voordat we hierin gaan.
uitzondering
een uitzondering is als een gebeurtenis die wordt gemaakt wanneer normale foutafhandeling het probleem niet kan oplossen.,Proberen om een getal te delen door nul of een tekort aan geheugen zijn voorbeelden van iets dat eenexceptie creëert. Soms maakt de auteur van de code die je gebruikt uitzonderingen voor bepaalde issues wanneer ze gebeuren.
gooien en vangen
wanneer een uitzondering optreedt, zeggen we dat een uitzondering wordt gegooid. Om een gegooid uitzondering af te handelen, moet je het vangen. Als een uitzondering wordt gegooid en het is niet gevangen door iets, het script stopt metexecuting.
de call stack
de call stack is de lijst met functies die elkaar hebben aangeroepen., Wanneer een functie wordt aangeroepen, wordt deze toegevoegd aan de stack of aan de top van de lijst. Wanneer de functie wordt afgesloten of geretourneerd, wordt deze uit de stack verwijderd.
wanneer een uitzondering wordt gegooid, wordt die call stack gecontroleerd om een exception handler te laten catchit.
Terminating and non-terminating errors
een uitzondering is over het algemeen een terminating errors. Een gegooid uitzondering is ofwel worden gevangen of hettermijnt de huidige uitvoering. Standaard wordt een niet-beëindigende fout gegenereerd door Write-Error
en het voegt een fout toe aan de uitvoerstroom zonder een uitzondering te gooien.,
Ik wijs hierop omdat Write-Error
en andere niet-eindigende fouten decatch
niet activeren.
een uitzondering doorslikken
Dit is wanneer u een fout ontdekt om deze te onderdrukken. Doe dit met de nodige voorzichtigheid, omdat het problemen met het opstarten erg moeilijk kan maken.
basic command syntaxis
Hier is een snel overzicht van de basic exception handling syntaxis gebruikt in PowerShell.
gooien
om onze eigen exception event te maken, gooien we een uitzondering met het throw
sleutelwoord.,
function Start-Something{ throw "Bad thing happened"}
Dit maakt een runtime-uitzondering aan die een beëindigingsfout is. Het wordt afgehandeld door een catch
in acalling functie of verlaat het script met een bericht als dit.
Write-Error-ErrorAction Stop
Ik zei dat Write-Error
standaard geen afsluitfout geeft. Als u-ErrorAction Stop
opgeeft, genereert Write-Error
een afsluitfout die kan worden afgehandeld met eencatch
.,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
dank aan Lee Dailey voor het herinneren aan het gebruik van -ErrorAction Stop
op deze manier.
Cmdlet-ErrorAction Stop
als u -ErrorAction Stop
opgeeft op een geavanceerde functie of cmdlet, worden alle Write-Error
verklaringen omgezet in het beëindigen van fouten die de uitvoering stoppen of die kunnen worden afgehandeld door een catch
.,
Start-Something -ErrorAction Stop
Try/Catch
de manier waarop exception handling werkt in PowerShell (en vele andere talen) is dat u eerst try
asectie van code en als het een fout geeft, kunt u catch
it. Hier is een snel voorbeeld.
het catch
script draait alleen als er een afsluitfout is. Als de try
correct wordt uitgevoerd, slaat het de catch
over.,
Try / Finally
soms moet je een fout niet verwerken, maar heb je wel wat code nodig om uit te voeren als een exceptionhappens of niet. Een finally
script doet precies dat.
kijk eens naar dit voorbeeld:
wanneer u een bron opent of verbindt, moet u deze sluiten. Als de uitzondering ExecuteNonQuery()
throwsan is, wordt de verbinding niet gesloten. Hier is dezelfde code in een try/finally
blok.
in dit voorbeeld wordt de verbinding gesloten als er een fout is. Het is ook gesloten als er noerror is., Hetfinally
script draait elke keer.
omdat je de uitzondering niet vangt, wordt het nog steeds doorgegeven op de call stack.
Try / Catch / Finally
het is perfect geldig om catch
en finally
samen te gebruiken. Meestal gebruik je het een of het ander, maar je kunt scenario ‘ s vinden waar je beide gebruikt.
$PSItem
nu we de basis uit de weg hebben, kunnen we wat dieper graven.,
binnen hetcatch
blok is er een automatische variabele ($PSItem
of$_
) van het typeErrorRecord
die de details over de uitzondering bevat. Hier is een kort overzicht van enkele van de keproperties.
voor deze voorbeelden gebruikte ik een Ongeldig pad in ReadAllText
om deze uitzondering te genereren.
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString ()
Dit geeft u het schoonste bericht om te gebruiken in loggen en algemene uitvoer., ToString()
wordt automatisch aangeroepen als $PSItem
in een tekenreeks wordt geplaatst.
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
Deze eigenschap bevat aanvullende informatie verzameld door PowerShell over de functie of scriptwaar de uitzondering werd gegooid. Hier is de InvocationInfo
uit de voorbeelduitzondering die Icreated.
de belangrijke details hier tonen deScriptName
, deLine
van de code en deScriptLineNumber
waar de aanroep begon.
$PSItem.,ScriptStackTrace
Deze eigenschap toont de volgorde van functieaanroepen die u naar de code bracht waar de uitzondering werd gegenereerd.
Ik maak alleen aanroepen naar functies in hetzelfde script, maar dit zou de oproepen volgen als er meerdere scripts bij betrokken waren.
$PSItem.Uitzondering
Dit is de werkelijke uitzondering die werd gegooid.
$PSItem.Uitzondering.Message
Dit is het algemene bericht dat de uitzondering beschrijft en een goed startpunt is bij het opstarten van problemen. De meeste uitzonderingen hebben een standaard bericht, maar kunnen ook worden ingesteld op iets aangepast wanneer de uitzondering wordt gegooid.,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
Dit is ook het bericht dat wordt geretourneerd bij het aanroepen van $PSItem.ToString()
als er niet één is ingesteld op deErrorRecord
.
$PSItem.Uitzondering.InnerException
uitzonderingen kunnen innerlijke uitzonderingen bevatten. Dit is vaak het geval wanneer de code die u noemt een uitzondering vangt en een andere uitzondering gooit. De oorspronkelijke uitzondering wordt in de nieuwe uitzondering geplaatst.
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
Ik zal hier later op terugkomen als ik het heb over het opnieuw gooien van uitzonderingen.
$PSItem.Uitzondering.,StackTrace
Dit is de StackTrace
voor de uitzondering. Ik toonde een ScriptStackTrace
hierboven, maar deze is voor de aanroepen naar beheerde code.
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 )
u krijgt deze stack trace alleen wanneer de gebeurtenis wordt gegooid vanuit beheerde code. Ik bel a .NetFramework functie direct dus dat is alles wat we kunnen zien in dit voorbeeld. Over het algemeen wanneer je kijkt naar een stack trace, je op zoek bent naar waar je code stopt en de systeemgesprekken beginnen.
werken met uitzonderingen
Er is meer aan uitzonderingen dan de basis syntaxis en uitzondering eigenschappen.,
getypte uitzonderingen vangen
u kunt selectief zijn met de uitzonderingen die u vangt. Uitzonderingen hebben een type en u kunt specifiekenhet type uitzondering dat u wilt vangen.
het type uitzondering wordt gecontroleerd voor elk catch
blok totdat er een is gevonden die overeenkomt met uw exception.It ‘ s belangrijk om te beseffen dat uitzonderingen kunnen erven van andere uitzonderingen. In het voorbeeld hierboven, heeftFileNotFoundException
erven van IOException
. Dus als de IOException
eerst was, dan zou het in plaats daarvan aangeroepen worden., Er wordt slechts één catch block aangeroepen, zelfs als er meerdere overeenkomsten zijn.
als we een System.IO.PathTooLongException
hadden, zou de IOException
overeenkomen, maar als we eenInsufficientMemoryException
hadden, dan zou niets het vangen en het zou de stack verspreiden.
meerdere typen tegelijk vangen
Het is mogelijk om meerdere soorten uitzonderingen te vangen met hetzelfdecatch
statement.
Dank u /u/Sheppard_Ra
Voor het suggereren van deze toevoeging.
getypte uitzonderingen gooien
u kunt getypte uitzonderingen in PowerShell gooien., In plaats van throw
met een tekenreeks aan te roepen:
throw "Could not find: $path"
Gebruik een Exception accelerator als volgt:
throw "Could not find: $path"
maar u moet een bericht opgeven wanneer u het op die manier doet.
u kunt ook een nieuwe instantie van een te gooien uitzondering maken. Het bericht is optioneel als je dit doet omdat het systeem standaard berichten heeft voor alle ingebouwde uitzonderingen.
throw ::new()throw ::new("Could not find path: $path")
Als u PowerShell 5.0 of hoger Niet gebruikt, moet u de oudere New-Object
benadering gebruiken.,
door een getypte uitzondering te gebruiken, kunt u (of anderen) de uitzondering vangen door het type zoals vermeld in de vorige sectie.
Write-Error-Exception
We kunnen deze getypte uitzonderingen toevoegen aan Write-Error
en we kunnen nog steeds catch
de fouten per exceptiontype. Gebruik Write-Error
zoals in de volgende voorbeelden:
dan kunnen we het als volgt vangen:
catch { Write-Log $PSItem.ToString()}
de grote lijst met .net-uitzonderingen
Ik heb een hoofdlijst samengesteld met behulp van de Reddit/r/PowerShell-gemeenschap die shundredds van bevat.,Netto uitzonderingen om dit bericht aan te vullen.
- De grote lijst met. net-uitzonderingen
Ik begin door die lijst te zoeken naar uitzonderingen die het gevoel hebben dat ze goed passen bij mysituation. Probeer uitzonderingen te gebruiken in de basis System
naamruimte.
uitzonderingen zijn objecten
Als u veel getypte uitzonderingen gaat gebruiken, onthoud dan dat het objecten zijn. Verschillende uitzonderingenhebben verschillende constructeurs en eigenschappen., Als we kijken naar het bestand niet gevonden Exceptiondocumentation voor System.IO.FileNotFoundException
, zien we dat we kunnen doorgeven in een bericht en een bestandspad.
::new("Could not find file", $path)
en het heeft een eigenschap FileName
die dat bestandspad blootlegt.
catch { Write-Output $PSItem.Exception.FileName}
raadpleeg de.net-documentatie voor andere constructors en objecteigenschappen.
een uitzondering opnieuw gooien
als alles wat je gaat doen in je catch
blok throw
dezelfde uitzondering is, doe dan niet catch
het., U dient alleen catch
een uitzondering die u van plan bent te behandelen of uit te voeren wanneer dit gebeurt.
Er zijn momenten waarop u een actie wilt uitvoeren op een uitzondering, maar opnieuw gooien de uitzondering sosomething downstream kan omgaan met het. We kunnen een bericht schrijven of het probleem inloggen dicht bij waar we het ontdekken, maar het probleem verder op de stack behandelen.
catch{ Write-Log $PSItem.ToString() throw $PSItem}
interessant genoeg kunnen we throw
aanroepen vanuit de catch
en het werpt de huidige uitzondering opnieuw.,
catch{ Write-Log $PSItem.ToString() throw}
We willen de uitzondering opnieuw gooien om de originele uitvoerinformatie zoals source scriptenregelnummer te behouden. Als we een nieuwe uitzondering op dit punt gooien, verbergt het waar de uitzondering begon.
een nieuwe uitzondering opnieuw gooien
als u een uitzondering vangt maar een andere wilt gooien, dan moet u de originele uitzondering in de nieuwe nestelen. Dit staat iemand op de stack toe om het te benaderen als de$PSItem.Exception.InnerException
.
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$pscmdlet.,ThrowTerminatingError ()
het enige wat ik niet leuk vind aan het gebruik van throw
voor ruwe uitzonderingen is dat de foutmeldingen op het throw
statement staan en aangeven dat de regel is waar het probleem is.
het hebben van de foutmelding vertel me dat mijn script is gebroken omdat ik throw
aangeroepen op Regel 31 is een abad-bericht voor gebruikers van uw script om te zien. Het vertelt ze niets nuttigs.
Dexter Dhami wees erop dat ikThrowTerminatingError()
kan gebruiken om dat te corrigeren.,
als we aannemen dat ThrowTerminatingError()
werd aangeroepen binnen een functie genaamd Get-Resource
, dan is dit de fout die we zouden zien.
ziet u hoe het wijst naar de functie Get-Resource
als de bron van het probleem? Dat vertelt de gebruiker iets nuttigs.
omdat $PSItem
een ErrorRecord
is, kunnen we ook ThrowTerminatingError
op deze manier gebruiken om opnieuw te gooien.,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
Dit verandert de bron van de fout in de Cmdlet en verbergt de interne gegevens van uw functie voor de gebruikers van uw Cmdlet.
Try can create terminating errors
Kirk Munro wijst erop dat sommige uitzonderingen alleen terminating errors zijn wanneer uitgevoerd in eentry/catch
blok. Hier is het voorbeeld dat hij me gaf dat een deel genereert door nul runtime uitzondering.
function Start-Something { 1/(1-1) }
roep het dan als volgt aan om te zien dat het de fout genereert en toch het bericht uitvoert.,
&{ Start-Something; Write-Output "We did it. Send Email" }
maar door dezelfde code in een try/catch
te plaatsen, zien we iets anders gebeuren.
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
we zien dat de fout een beëindigingsfout wordt en niet het eerste bericht wordt uitgevoerd. Wat ik hier niet leuk vind is dat je deze code in een functie kunt hebben en het werkt anders als iemand een try/catch
gebruikt.
Ik heb hier zelf geen problemen mee gehad, maar het is een hoekgeval om me ervan bewust te zijn.
$pscmdlet.,ThrowTerminatingError () inside try/catch
een nuance van $PSCmdlet.ThrowTerminatingError()
is dat het een afsluitende fout maakt binnen yourCmdlet, maar het verandert in een niet-afsluitende fout nadat het uw Cmdlet verlaat. Dit laat de lastop de beller van uw functie om te beslissen hoe de fout te behandelen. Ze kunnen het terugdraaien in een atermineerfout door -ErrorAction Stop
te gebruiken of het aan te roepen vanuit een try{...}catch{...}
.,
Public function templates
een laatste manier die ik had met mijn gesprek met Kirk Munro was dat hij eentry{...}catch{...}
rond elke begin
, process
en end
blok in al zijn geavanceerde functies. In deze algemene vangblokken heeft hij een enkele regel die$PSCmdlet.ThrowTerminatingError($PSItem)
gebruikt om alle uitzonderingen af te handelen die zijn functies verlaten.
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
omdat alles in een try
statement staat binnen zijn functies, werkt alles consistent., Dit geeft ook schone fouten aan de eindgebruiker die de interne code van de gegenereerde fout verbergt.
Trap
Ik richtte me op het try/catch
aspect van uitzonderingen. Maar er is een erfenis die ik moet vermelden voordat we dit afronden.
A trap
wordt in een script of functie geplaatst om alle uitzonderingen op te vangen die in dat bereik voorkomen. Als er een uitzondering is, wordt de code in de trap
uitgevoerd en dan gaat de normale code verder. Als meerdere uitzonderingen gebeuren, dan wordt de val keer op keer genoemd.,
persoonlijk heb ik deze aanpak nooit gevolgd, maar ik zie de waarde in admin of controller scripts die alle uitzonderingen registreren en dan nog steeds uitvoeren.
slotopmerkingen
het toevoegen van de juiste exception handling aan uw scripts maakt ze niet alleen stabieler, maar maakt het ook makkelijker voor u om deze uitzonderingen op te lossen.
Ik heb veel tijd besteed aan het praten over throw
omdat het een kernconcept is als het gaat om exceptionhandling., PowerShell gaf ons ook Write-Error
die alle situaties behandelt waarin uthrow
zou gebruiken. Dus denk niet dat je throw
moet gebruiken na het lezen van dit.
nu ik de tijd heb genomen om in dit detail over de afhandeling van uitzonderingen te schrijven, ga ik over op het gebruik van Write-Error -Stop
om fouten in mijn code te genereren. Ik ga ook het advies van Kirk overnemen en ThrowTerminatingError
mijn Goto exception handler maken voor elke functie.