Welcome to Our Website

wszystko, co chciałeś wiedzieć o wyjątkach

  • 05/23/2020
  • 14 minut do przeczytania
    • j
    • f
    • s

obsługa błędów jest częścią życia, jeśli chodzi o pisanie kodu. Często możemy sprawdzić i zweryfikować warunki oczekiwanego zachowania. Gdy dzieje się coś nieoczekiwanego, przechodzimy do obsługi wyjątków., Możesz łatwo obsługiwać wyjątki generowane przez KOD innych osób lub możesz generować własne wyjątki dla innych do obsługi.

Uwaga

Oryginalna wersja tego artykułu pojawiła się na blogu napisanym przez @KevinMarquette. Zespół ThePowerShell dziękuje Kevinowi za udostępnienie nam tych treści. Zapraszam na jego bloga atPowerShellExplained.com.

podstawowa terminologia

zanim przejdziemy do tej terminologii, musimy omówić kilka podstawowych terminów.

wyjątek

wyjątek jest jak zdarzenie, które jest tworzone, gdy normalna obsługa błędów nie może poradzić sobie z problemem.,Próba podzielenia liczby przez zero lub brak pamięci to przykłady czegoś, co tworzy wyjątek. Czasami autor kodu, którego używasz, tworzy wyjątki dla pewnych problemów, gdy się one zdarzają.

Throw and Catch

gdy wystąpi wyjątek, mówimy, że wyjątek jest wyrzucany. Aby obsłużyć wyrzucony wyjątek, musisz go złapać. Jeśli wyjątek zostanie wyrzucony i nie zostanie złapany przez coś, skrypt przestanie działać.

stos wywołań

stos wywołań jest listą funkcji, które wywołały się nawzajem., Gdy funkcja jest wywoływana, jest dodawana do stosu lub na górze listy. Gdy funkcja kończy lub powraca, jest usuwana ze stosu.

gdy wyjątek jest wyrzucany, ten stos wywołań jest sprawdzany w celu przechwycenia funkcji obsługi wyjątków.

błędy kończące i nie kończące

wyjątkiem jest zazwyczaj błąd kończący. Rzucony wyjątek jest albo be caught, albo kończy bieżące wykonanie. Domyślnie, błąd nie kończący jest generowany przez Write-ErrorI dodaje błąd do strumienia wyjściowego bez rzucania wyjątku.,

zwracam na to uwagę, ponieważWrite-ErrorI inne błędy nie kończące nie powodującatch.

Połykanie wyjątku

To jest, gdy wyłapujesz błąd tylko po to, aby go stłumić. Zrób to z ostrożnością, ponieważ może to sprawić, że problemy z roubleshooting będą bardzo trudne.

Podstawowa składnia poleceń

oto krótki przegląd podstawowej składni obsługi wyjątków używanej w PowerShell.

Throw

aby utworzyć własne Zdarzenie wyjątku, rzucamy wyjątek ze słowem kluczowym throw.,

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

spowoduje to utworzenie wyjątku uruchomieniowego, który jest błędem kończącym działanie. Jest obsługiwany przez catch w funkcji wywołania lub kończy skrypt z takim Komunikatem.

Write-Error-ErrorAction Stop

wspomniałem, żeWrite-Error domyślnie nie wyświetla błędu zakończenia. Jeśli podasz-ErrorAction Stop, Write-Errorgeneruje błąd zakończenia, który może być obsługiwany za pomocącatch.,

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

Dziękujemy Lee Dailey za przypomnienie o użyciu-ErrorAction Stop w ten sposób.

Cmdlet-ErrorAction Stop

Jeśli podasz -ErrorAction Stop na dowolnej zaawansowanej funkcji lub cmdletie, zmieni to wszystkie Write-Errorna błędy kończące, które zatrzymują wykonanie lub które mogą być obsługiwane przez catch.,

Start-Something -ErrorAction Stop

Try/Catch

sposób obsługi wyjątków działa w PowerShell (i wielu innych językach) jest to, że najpierwtryjako sekcja kodu i jeśli rzuca błąd, możnacatch to. Oto szybka próbka.

skryptcatch uruchamia się tylko wtedy, gdy wystąpił błąd zakończenia. Jeśli try wykonuje się poprawnie, to pomija catch.,

Try/Finally

czasami nie musisz obsługiwać błędu, ale nadal potrzebujesz kodu do wykonania, jeśli exceptionhappens lub nie. Skryptfinally robi dokładnie to.

spójrz na ten przykład:

za każdym razem, gdy otwierasz lub łączysz się z zasobem, powinieneś go zamknąć. JeśliExecuteNonQuery() rzuca wyjątek, połączenie nie jest zamknięte. Oto ten sam kod wewnątrz bloku try/finally.

w tym przykładzie połączenie zostanie zamknięte, jeśli wystąpi błąd. Jest również zamknięty, jeśli nie ma noerror., Skryptfinally jest uruchamiany za każdym razem.

ponieważ nie łapiesz wyjątku, to i tak dostaje się do stosu wywołań.

spróbuj/Złap/wreszcie

doskonale jest używaćcatch Ifinally razem. Przez większość czasu będziesz używać jednego lub drugiego, ale możesz znaleźć scenariusze, w których używasz obu.

$PSItem

teraz, gdy mamy podstawy z drogi, możemy kopać trochę głębiej.,

wewnątrz blokucatch znajduje się zmienna automatyczna ($PSItem lub$_) typuErrorRecord, która zawiera szczegóły dotyczące wyjątku. Oto szybki przegląd niektórych klawiatur.

dla tych przykładów użyłem nieprawidłowej ścieżki wReadAllText do wygenerowania tego wyjątku.

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

PSItem.ToString()

to daje najczystszą wiadomość do użycia w logowaniu i ogólnym wyjściu., ToString() izautomatycznie wywołany, jeśli$PSItem jest umieszczony wewnątrz łańcucha.

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

$PSItem.InvocationInfo

Ta właściwość zawiera dodatkowe informacje zebrane przez PowerShell o funkcji lub skrypcie, gdzie został wyrzucony wyjątek. Poniżej znajduje sięInvocationInfo z przykładowego wyjątku, który Icreated.

ważne szczegóły tutaj pokazują ScriptName, Linekodu i ScriptLineNumber gdzie rozpoczęło się wywołanie.

$PSItem.,ScriptStackTrace

Ta właściwość pokazuje kolejność wywołań funkcji, które doprowadziły do kodu, w którym został wygenerowany wyjątek.

wykonuję tylko wywołania do funkcji w tym samym skrypcie, ale to śledziłoby wywołania, gdyby zaangażowane były Skrypty multipleksowe.

$PSItem.Wyjątek

jest to rzeczywisty wyjątek, który został wyrzucony.

$PSItem.Wyjątek.Message

jest to ogólna wiadomość opisująca wyjątek i stanowi dobry punkt wyjścia podczas roubleshootingu. Większość WYJĄTKÓW ma domyślną wiadomość, ale może być również ustawiona na coś niestandardowego, gdy wyjątek jest wyrzucany.,

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

jest to również wiadomość zwracana podczas wywoływania $PSItem.ToString()jeśli naErrorRecord nie było żadnego ustawionego.

$PSItem.Wyjątek.Wyjątki InnerException

mogą zawierać wewnętrzne wyjątki. Często dzieje się tak, gdy kod, który wywołujesz, przyciąga wyjątek i rzuca inny wyjątek. Oryginalny wyjątek jest umieszczany wewnątrz nowego wyjątku.

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

wrócę do tego później, gdy będę mówił o ponownym wyrzucaniu WYJĄTKÓW.

$PSItem.Wyjątek.,StackTrace

To jest StackTrace dla wyjątku. Pokazałem ScriptStackTrace powyżej, ale ten jest dla wywołania zarządzanego kodu.

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 )

ten ślad stosu otrzymuje się tylko wtedy, gdy zdarzenie jest wyrzucane z kodu zarządzanego. Dzwonię do A.Funkcja NETframework działa bezpośrednio, więc to wszystko, co widzimy w tym przykładzie. Ogólnie rzecz biorąc, gdy patrzysz na ślad stosu, szukasz miejsca, w którym kończy się kod i zaczynają się połączenia systemowe.

praca z wyjątkami

wyjątki są czymś więcej niż tylko podstawową składnią i właściwościami WYJĄTKÓW.,

Przechwytywanie typowanych WYJĄTKÓW

możesz być selektywny z wyjątkami, które wyłapujesz. Wyjątki mają typ i możesz określićtyp wyjątku, który chcesz złapać.

typ wyjątku jest sprawdzany dla każdegocatch bloku, dopóki nie zostanie znaleziony jeden, który pasuje do twojego exception.It ważne jest, aby zdać sobie sprawę, że wyjątki mogą dziedziczyć po innych wyjątkach. W powyższym przykładzieFileNotFoundException dziedziczy z IOException. Więc jeśli IOException był pierwszy, to zostanie wywołany., Tylko jeden blok catch jest wywoływany, nawet jeśli istnieje wiele dopasowań.

gdybyśmy mieli System.IO.PathTooLongException, IOExceptionpasowałby, ale gdybyśmy mieliInsufficientMemoryException to nic by go nie złapało i propagowałoby się na stosie.

Przechwytywanie wielu typów jednocześnie

możliwe jest przechwytywanie wielu typów wyjątków za pomocą tej samej instrukcjicatch.

Dziękuję za zasugerowanie tego dodatku.

rzucanie wpisanych WYJĄTKÓW

możesz rzucać wpisane wyjątki w PowerShell., Zamiast wywoływaćthrow ciągiem znaków:

throw "Could not find: $path"

użyj akceleratora WYJĄTKÓW takiego jak:

throw "Could not find: $path"

ale musisz podać wiadomość, gdy robisz to w ten sposób.

można również utworzyć nową instancję wyjątku do wyrzucenia. Wiadomość jest opcjonalna, ponieważ system ma domyślne wiadomości dla wszystkich wbudowanych WYJĄTKÓW.

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

Jeśli nie używasz PowerShell 5.0 lub wyższej, musisz użyć starszego podejściaNew-Object.,

używając typowanego wyjątku, możesz (lub inni) wyłapać wyjątek według typu, o którym mowa w sekcji previous.

Write-Error-Exception

możemy dodać te wpisane wyjątki doWrite-Error I nadal możemycatch błędów przez exceptiontype. Użyj Write-Error jak w tych przykładach:

następnie możemy go złapać w następujący sposób:

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

duża lista wyjątków .NET

skompilowałem listę główną z pomocą społeczności Reddit/r/PowerShell, która zawiera.,NET exceptions to complete this post.

  • duża lista wyjątków.NET

zaczynam od wyszukania na tej liście wyjątków, które wydają się być dobre dla mysituation. Powinieneś spróbować użyć WYJĄTKÓW w podstawowej przestrzeni nazw System.

wyjątki to obiekty

Jeśli zaczniesz używać wielu wpisywanych WYJĄTKÓW, pamiętaj, że są to obiekty. Różne wyjątki mają różne konstruktory i właściwości., Jeśli spojrzymy na dokument FileNotFoundExceptiondocumentation dla System.IO.FileNotFoundException, zobaczymy, że możemy przekazać wiadomość i ścieżkę pliku.

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

i ma właściwośćFileName, która wyświetla tę ścieżkę do pliku.

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

należy zapoznać się z dokumentacją.NET dla innych konstruktorów i właściwości obiektu.

ponowne rzucenie wyjątku

Jeśli wszystko, co masz zamiar zrobić w swoim catchbloku jest throwten sam wyjątek, to nie catch to., Należy tylkocatch wyjątek, który planujesz obsłużyć lub wykonać jakąś akcję, gdy się pojawi.

są momenty, w których chcesz wykonać akcję na wyjątku, ale ponowne wyrzucenie wyjątku może z tym poradzić. Możemy napisać wiadomość lub zapisać problem blisko miejsca, w którym go odkryliśmy, ale poradzimy sobie z problemem dalej na stosie.

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

Co ciekawe, możemy wywołaćthrowz poziomucatch I ponownie rzuca currentexception.,

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

chcemy ponownie wyrzucić wyjątek, aby zachować oryginalne informacje o wykonaniu, takie jak skrypt źródłowy i numer linii. Jeśli w tym momencie rzucimy nowy wyjątek, ukryje się on w miejscu, w którym wyjątek się rozpoczął.

ponowne rzucenie nowego wyjątku

Jeśli złapiesz wyjątek, ale chcesz rzucić inny, powinieneś zagnieżdżać oryginalną wyjątek wewnątrz nowego. Pozwala to komuś w dół stosu uzyskać do niego dostęp jako$PSItem.Exception.InnerException.

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

$PSCmdlet.,ThrowTerminatingError ()

jedyną rzeczą, której nie lubię w używaniu throw dla surowych wyjątków jest to, że messagepoints błędu w throw I wskazuje, że linia jest tam, gdzie problem.

mając komunikat o błędzie powiedz mi, że mój skrypt jest uszkodzony, ponieważ zadzwoniłemthrow on line 31 jest komunikat abad dla użytkowników skryptu, aby zobaczyć. Nie mówi im nic użytecznego.

Dexter Dhami wskazał, że mogę użyćThrowTerminatingError(), aby to poprawić.,

jeśli założymy, że ThrowTerminatingError()został wywołany wewnątrz funkcji o nazwieGet-Resource, to jest to błąd, który możemy zobaczyć.

Czy widzisz jak wskazuje na funkcjęGet-Resource jako źródło problemu? To mówi użytkownikowi coś użytecznego.

ponieważ$PSItemjestErrorRecord, możemy również użyćThrowTerminatingError w ten sposób ponownie rzucić.,

catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}

powoduje to zmianę źródła błędu na Cmdlet i ukrycie wewnętrznych funkcji przed użytkownikami Twojego Cmdlet.

Try może wytworzyć błędy zakończenia

Kirk Munro zwraca uwagę, że niektóre wyjątki to tylko błędy zakończenia, gdy są wykonywane wewnątrz blokutry/catch. Oto przykład, który dał mi, który generuje wyjątek Runtime divide by zero.

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

następnie wywołaj go w ten sposób, aby zobaczyć, jak generuje błąd i nadal wysyła wiadomość.,

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

ale umieszczając ten sam kod wewnątrz try/catch, widzimy coś innego.

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

widzimy, że błąd staje się błędem kończącym i nie wysyła pierwszej wiadomości. Nie podoba mi się to, że możesz mieć ten kod w funkcji i działa ON inaczej, jeśli ktoś używa try/catch.

sam nie miałem z tym problemów, ale warto o tym pamiętać.

$PSCmdlet.,ThrowTerminatingError () wewnątrz try/catch

jednym z niuansów$PSCmdlet.ThrowTerminatingError() jest to, że tworzy błąd kończący w yourCmdlet, ale zmienia się w błąd nie kończący po opuszczeniu Twojego Cmdlet. To pozostawia burdenon rozmówcy funkcji, aby zdecydować, jak poradzić sobie z błędem. Mogą zmienić go z powrotem w błąd aterminacji, używając -ErrorAction Stop lub wywołując go z poziomu try{...}catch{...}.,

szablony funkcji publicznych

ostatni sposób miałem z mojej rozmowy z Kirk Munro było to, że umieszczatry{...}catch{...} wokół każdego begin, process I end Blokuj wszystkie jego zaawansowane funkcje. W tych typowych blokach catch ma pojedynczą linię używając$PSCmdlet.ThrowTerminatingError($PSItem), aby poradzić sobie ze wszystkimi wyjątkami opuszczającymi jego funkcje.

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

ponieważ wszystko jest wtry w jego funkcjach wszystko działa konsekwentnie., To również daje czyste błędy użytkownikowi końcowemu, który ukrywa kod wewnętrzny z wygenerowanego błędu.

Pułapka

skupiłem się natry/catch aspekcie WYJĄTKÓW. Ale jest jedna funkcja, o której muszę wspomnieć, zanim to zakończymy.

atrap jest umieszczany w skrypcie lub funkcji, aby wychwycić wszystkie wyjątki występujące w tym zakresie. Gdy wystąpi wyjątek, kod w trap jest wykonywany, a następnie normalny kod jest kontynuowany. Jeśli zdarzy się wiele wyjątków, to pułapka jest wywoływana w kółko.,

ja osobiście nigdy nie zastosowałem tego podejścia, ale widzę wartość w skryptach admin lub controller, które nie zawierają żadnych WYJĄTKÓW, a następnie nadal są wykonywane.

Uwagi końcowe

dodanie odpowiedniej obsługi wyjątków do skryptów nie tylko czyni je bardziej stabilnymi, ale także ułatwia rozwiązywanie problemów z tymi wyjątkami.

spędziłem dużo czasu rozmawiającthrow ponieważ jest to podstawowa koncepcja, gdy mówimy o exceptionhandling., PowerShell dał nam również Write-Error, który obsługuje wszystkie sytuacje, w których można użyćthrow. Więc nie myśl, że musisz używać throw po przeczytaniu tego.

teraz, gdy poświęciłem czas, aby napisać o obsłudze WYJĄTKÓW w tym szczególe, zamierzam przełączyć się na użycieWrite-Error -Stop do generowania błędów w moim kodzie. Zamierzam również skorzystać z rady i zrobić ThrowTerminatingError mój program obsługi wyjątków goto dla każdej funkcji.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *