Welcome to Our Website

Alles wat u al wilde weten over de uitzonderingen

  • 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-Erroren het voegt een fout toe aan de uitvoerstroom zonder een uitzondering te gooien.,

Ik wijs hierop omdat Write-Error en andere niet-eindigende fouten decatchniet 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-Erroreen 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-Errorverklaringen 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 catchover.,

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 typeErrorRecorddie 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 deScriptLineNumberwaar 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 catchhet., 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/catchgebruikt.

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 uthrowzou 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.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *