- 05/23/2020
- 14 minut číst
-
- j
- f
zpracování Chyb je jen součástí života, když dojde na psaní kódu. Často můžeme zkontrolovat a ověřitpodmínky pro očekávané chování. Když dojde k neočekávanému, obrátíme se na manipulaci s výjimkami., Youcan snadno zvládnout výjimky generované kódem jiných lidí, nebo si můžete vytvořit své vlastníexceptions pro ostatní zvládnout.
Poznámka:
původní verze tohoto článku vyšla na blogu napsal @KevinMarquette. Tým ThePowerShell děkuje Kevinovi za sdílení tohoto obsahu s námi. Podívejte se prosím na jeho blog atPowerShellExplained.com.
základní terminologie
musíme pokrýt některé základní pojmy, než skočíme do tohoto.
výjimka
výjimka je jako událost, která je vytvořena, když normální zpracování chyb nemůže problém vyřešit.,Pokus o rozdělení čísla nulou nebo vyčerpání paměti jsou příklady něčeho, co vytváří anexcepci. Někdy autor kódu, který používáte, vytváří výjimky pro určité problémykdyž k nim dojde.
házet a chytit
když dojde k výjimce, říkáme, že je vyvolána výjimka. Chcete-li zvládnout hozenou výjimku, vyje třeba ji chytit. Pokud je výjimka hozena a není něčím chycena, skript se zastaví.
zásobník hovorů
zásobník hovorů je seznam funkcí, které se navzájem volaly., Když je funkce volána, je tozdostane se přidán do zásobníku nebo do horní části seznamu. Když funkce ukončí nebo vrátí, je odstraněnaz zásobníku.
je-li vyvolána výjimka, je tento zásobník volání zkontrolován, aby mohl Správce výjimek chytit.
chyby ukončení a ukončení
výjimka je obecně chyba ukončení. Hozená výjimka je buď chycena, nebo topřekončí aktuální provedení. Ve výchozím nastavení je ukončující chyba generována Write-Error
a přidává chybu do výstupního proudu bez vyhazování výjimky.,
tento bod, protože Write-Error
a jiné non-ukončení chyby nejsou spoušťcatch
.
polykání výjimky
to je, když chytíte chybu jen proto, abyste ji potlačili. Udělejte to opatrně, protože to může udělattroupleshooting problémy velmi obtížné.
syntaxe základního příkazu
zde je rychlý přehled Základní syntaxe zpracování výjimek používané v PowerShellu.
hodit
Chcete-li vytvořit vlastní událost výjimky, hodíme výjimku sthrow
Klíčové slovo.,
function Start-Something{ throw "Bad thing happened"}
tím se vytvoří runtime výjimka, která je ukončující chyba. Je zpracováncatch
ve funkci acalling nebo ukončí skript se zprávou, jako je tato.
Write-Error-Erroraction Stop
zmínil jsem se, že Write-Error
ve výchozím nastavení nevyhodí chybu ukončení. Pokud zadáte-ErrorAction Stop
Write-Error
generuje ukončení chyby, které mohou být řešeny pomocícatch
.,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
Děkuji Lee Dailey za připomenutí použití -ErrorAction Stop
tímto způsobem.
Cmdlet -ErrorAction
Pokud zadáte -ErrorAction Stop
na nějaké pokročilé funkce nebo rutiny, to se změní vše Write-Error
prohlášení do ukončení chyby, které se zastavení exekuce nebo které mohou být řešeny pomocí catch
.,
Start-Something -ErrorAction Stop
Try/Catch
způsob zpracování výjimek funguje v PowerShell (a mnoho jiných jazyků) je, že první try
asection kódu a pokud to vyhodí chybu, můžete catch
. Zde je rychlý vzorek.
catch
skript běží pouze v případě, že dojde k chybě ukončení. Pokud se try
provede správně, přeskočí catch
.,
zkuste / konečně
někdy nemusíte zpracovávat chybu, ale stále potřebujete nějaký kód k provedení, pokud se exceptionhappens nebo ne. finally
skript dělá přesně to.
podívejte se na tento příklad:
kdykoli otevřete nebo se připojíte ke zdroji, měli byste jej zavřít. PokudExecuteNonQuery()
hodí výjimku, připojení není uzavřeno. Zde je stejný kód uvnitř blokutry/finally
.
v tomto příkladu je připojení uzavřeno, pokud dojde k chybě. Je také uzavřen, pokud neexistuje žádná chyba., finally
skript běží pokaždé.
protože výjimku nezachytíte, stále se šíří do zásobníku hovorů.
Try/Catch/Konečně
je naprosto platný pomocí catch
finally
dohromady. Většinu času budete používat jeden nebodruhý, ale můžete najít scénáře, kde používáte oba.
$ PSItem
nyní, když jsme dostali základy z cesty, můžeme kopat trochu hlouběji.,
Uvnitř catch
blok, tam je automatické proměnné ($PSItem
nebo $_
) typ ErrorRecord
, který obsahuje podrobnosti o výjimce. Zde je rychlý přehled některých klávesnicvlastnosti.
pro tyto příklady jsem použil neplatnou cestu v ReadAllText
pro generování této výjimky.
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString ()
to vám dává nejčistší zprávu, kterou chcete použít při protokolování a obecném výstupu., ToString()
isautomatically volal, jestli $PSItem
je umístěn uvnitř řetězce.
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
tato vlastnost obsahuje další informace shromážděné PowerShell o funkci nebo scriptwhere výjimka byla hozena. Zde je InvocationInfo
ze vzorové výjimky, kterou Icreated.
důležité informace zde ScriptName
Line
kód ScriptLineNumber
, kde je aplikace spuštěna.
$PSItem.,ScriptStackTrace
Tato vlastnost ukazuje pořadí volání funkcí, které musíš kódu, kde výjimkou wasgenerated.
volám pouze na funkce ve stejném skriptu, ale to by sledovalo hovory, pokud by se jednalo o multiplescripts.
$PSItem.Výjimka
toto je skutečná výjimka, která byla hozena.
$PSItem.Výjimek.Zpráva
toto je obecná zpráva, která popisuje výjimku a je dobrým výchozím bodem, kdyžpřestřelování. Většina výjimek má výchozí zprávu, ale může být také nastavena na něco, co je vlastní, kdyvyloučení výjimky.,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
toto je také zpráva vrácená při volání $PSItem.ToString()
pokud naErrorRecord
.
$PSItem.Výjimek.Innerexception
výjimky mohou obsahovat vnitřní výjimky. To je často případ, kdy kód, který volátecatches výjimku a hodí jinou výjimku. Původní výjimka je umístěna uvnitřnová výjimka.
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
vrátím se k tomu později, když mluvím o opakovaných výjimkách.
$PSItem.Výjimek.,StackTrace
toto jeStackTrace
pro výjimku. Ukázal jsem ScriptStackTrace
výše, ale tohle je forthe volání do spravovaného kódu.
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 )
tuto stopu získáte pouze tehdy, když je událost vyvolána ze spravovaného kódu. Volám a .NETframework funguje přímo tak, že je vše, co můžeme vidět v tomto příkladu. Obecně, když se díváte na stopu zásobníku, hledáte místo, kde se váš kód zastaví a začnou systémové hovory.
práce s výjimkami
existuje více výjimek než základní vlastnosti syntaxe a výjimky.,
chytání zadaných výjimek
můžete být selektivní s výjimkami, které chytíte. Výjimky mají typ a můžete specifikovattyp výjimky, kterou chcete chytit.
typ výjimky je kontrolována pro každou catch
blokovat, dokud jeden je zjištěno, že odpovídá vašemu výjimkou.Je důležité si uvědomit, že výjimky mohou dědit od jiných výjimek. Ve výše uvedeném příkladuFileNotFoundException
dědí z IOException
. Takže pokud IOException
byl první, pak by se místo toho zavolal., Pouze jeden blok úlovku je vyvolán, i když existuje více zápasů.
Jestliže máme System.IO.PathTooLongException
IOException
by zápas, ale kdybychom měliInsufficientMemoryException
pak by se nic chytit, a to bude šířit do zásobníku.
chytit více typů najednou
je možné zachytit více typů výjimek se stejnýmcatch
prohlášení.
Děkuji /u/Sheppard_Ra
za návrh tohoto přidání.
házení zadané výjimky
můžete házet zadané výjimky v PowerShell., Namísto volání throw
s string:
throw "Could not find: $path"
Použít výjimku akcelerátoru, jako je tento:
throw "Could not find: $path"
Ale musíte zadat zprávu, když budete dělat to, že způsob,.
můžete také vytvořit novou instanci výjimky, která má být hozena. Zpráva je volitelná, když to uděláteto proto, že systém má výchozí zprávy pro všechny vestavěné výjimky.
throw ::new()throw ::new("Could not find path: $path")
Pokud nepoužíváte PowerShell 5.0 nebo vyšší, musíte použít starší New-Object
přístup.,
pomocí zadané výjimky můžete výjimku (nebo jiné) zachytit podle typu, jak je uvedeno v úvodní části.
Write-Error-Výjimka:
můžeme sečíst tyto zadané výjimky Write-Error
a ještě můžeme catch
chyby exceptiontype. Používání Write-Error
, jako v těchto příkladech:
Pak se můžeme chytit.
catch { Write-Log $PSItem.ToString()}
velký seznam .NET výjimky
jsem sestavil seznam s pomocí Reddit/r/PowerShell společenství, které containshundreds .,Čisté výjimky k doplnění tohoto příspěvku.
- velký seznam výjimek. Net
začnu hledáním tohoto seznamu výjimek, které mají pocit, že by byly vhodné pro mystituaci. Měli byste se pokusit použít výjimky v základně System
jmenný prostor.
Výjimky jsou objekty
Pokud začnete používat hodně zadali výjimky, pamatujte, že jsou objekty. Různé výjimkymají různé konstruktory a vlastnosti., Pokud se podíváme na FileNotFoundExceptiondocumentation System.IO.FileNotFoundException
vidíme, že můžeme předat zprávu a s filepath.
::new("Could not find file", $path)
A FileName
vlastnost, která odhaluje, že cesta k souboru.
catch { Write-Output $PSItem.Exception.FileName}
měli byste se podívat na dokumentaci. NET pro jiné konstruktory a vlastnosti objektů.
Re-házení výjimku
Pokud se chystáte udělat v catch
blok throw
stejná výjimka, pak si nenechte catch
., Měli byste pouze catch
výjimku, kterou plánujete zvládnout nebo provést nějakou akci, když se to stane.
tam jsou časy, kdy chcete provést akci na výjimku, ale re-hodit výjimku sosomething downstream může vypořádat s ním. Mohli bychom napsat zprávu nebo zaznamenat problém poblíž místa, kde ho objevíme,ale problém dále vyřešíme.
catch{ Write-Log $PSItem.ToString() throw $PSItem}
je Zajímavé, že je můžeme nazvat throw
v catch
a znovu hází currentexception.,
catch{ Write-Log $PSItem.ToString() throw}
chceme znovu hodit výjimku, abychom zachovali původní informace o provedení, jako je číslo řádku source scriptand. Pokud v tomto okamžiku hodíme novou výjimku, skrývá se tam, kde začala výjimka.
Re-házení nový výjimka:
Pokud jste chytit výjimku, ale chcete hodit jiný, pak byste měli hnízdo na originalexception uvnitř nové. To umožňuje někomu v zásobníku přístup jako$PSItem.Exception.InnerException
.
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$PSCmdlet.,ThrowTerminatingError()
jedna věc, kterou nemám rád o používání throw
raw výjimky je, že chyba messagepoints na throw
prohlášení a označuje, že linka je místo, kde problém je.
Má chybovou zprávu řekni mi, že můj scénář je rozbité, protože jsem volal throw
na řádku 31 je abad zpráva pro uživatele skriptu vidět. Neříká jim to nic užitečného.
Dexter Dhami poukázal na to, že k nápravě mohu použít ThrowTerminatingError()
.,
pokud předpokládáme, že ThrowTerminatingError()
byla volána uvnitř funkce s názvem Get-Resource
, pakto je chyba, kterou bychom viděli.
vidíte, jak ukazuje na funkci Get-Resource
jako zdroj problému? To říká uživateli něco užitečného.
, Protože $PSItem
ErrorRecord
, můžeme také použít ThrowTerminatingError
je to způsob, jak znovu hodit.,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
tím se změní zdroj chyby, aby se Rutiny a skrýt vestavby své funkce z uživatelů své Rutiny.
Zkuste lze vytvořit ukončení, chyby
Kirk Munro poukazuje na to, že nějaké výjimky jsou pouze ukončení chyby při spuštění uvnitřtry/catch
blok. Zde je příklad, který mi dal, který generuje dělbu nulovou výjimkou runtime.
function Start-Something { 1/(1-1) }
pak ji vyvolat takhle vidět generovat chybu a stále výstup zprávy.,
&{ Start-Something; Write-Output "We did it. Send Email" }
ale umístěním stejného kódu do try/catch
vidíme, že se stalo něco jiného.
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
vidíme, že chyba se stala ukončení chyba a ne výstup první zprávu. Co se mi nelíbí, je to, že tento kód můžete mít ve funkci a funguje jinak, pokud někdo používá try/catch
.
sám jsem se s tím nesetkal, ale je to rohový případ, o kterém je třeba vědět.
$PSCmdlet.,ThrowTerminatingError() uvnitř try/catch
Jeden nuance $PSCmdlet.ThrowTerminatingError()
je, že vytváří ukončení chyba v yourCmdlet ale to se změní na non-ukončení chybě poté, co opustí své Rutiny. To ponechává burdenon volajícího vaší funkce rozhodnout, jak zvládnout chybu. Mohou jej vrátit zpět do aterminating chyby pomocí -ErrorAction Stop
nebo volat z try{...}catch{...}
.,
Veřejné funkce šablony
Jeden poslední cestu jsem měl s mým rozhovor s Kirkem Munro byl, že on místechtry{...}catch{...}
každý begin
process
end
blok ve všech jeho advancedfunctions. V těchto obecných catch bloky, má jeden řádek pomocí$PSCmdlet.ThrowTerminatingError($PSItem)
řešit všechny výjimky, takže jeho funkce.
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
protože vše je v try
prohlášení v rámci svých funkcí, vše funguje důsledně., Tototaké dává koncovému uživateli čisté chyby, které skryjí interní kód před generovanou chybou.
zaměřil jsem se natry/catch
aspekt výjimek. Ale je tu jedna funkce dědictví, kterou musím zmínit, než to zabalíme.
a trap
je umístěn ve skriptu nebo funkci zachytit všechny výjimky, které se dějí v tomto rozsahu. Když dojde k výjimce, provede se kód v trap
a pak pokračuje normální kód. Pokud dojde k několika výjimkám, pak se past nazývá znovu a znovu.,
osobně jsem tento přístup nikdy nepřijal, ale vidím hodnotu ve skriptech admin nebo controller, které obsahují všechny výjimky, a pak stále pokračují v provádění.
závěrečné poznámky
přidání správného zacházení s výjimkami do skriptů je nejen činí stabilnějšími,ale také usnadňuje řešení těchto výjimek.
strávil jsem spoustu času mluvením throw
protože je to základní koncept, když mluvíme o výjimcehandling., PowerShell nám také dal Write-Error
který zpracovává všechny situace, kdy byste použilithrow
. Takže si nemyslete, že po přečtení musíte používat throw
.
Teď, když to mám vzít čas na to psát o zpracování výjimek v tomto detailu, jdu to přeřadit během používání Write-Error -Stop
generovat chyby v mém kódu. Jdu také na radu takeKirk a udělám ThrowTerminatingError
můj Goto Exception handler pro každou funkci.