Centrum Skryptów - Systemy Operacyjne

Jak używać bloków Try/Catch/Finally w programie Windows PowerShell?

Udostępnij na: Facebook

Skrypciarze odpowiadają na Wasze pytania

Witamy w rubryce TechNet, w której Skrypciarze z firmy Microsoft odpowiadają na częste pytania dotyczące używania skryptów w administracji systemu. Jeśli macie jakieś pytania z tej dziedziny, zachęcamy do wysłania e-maila na adres: scripter@microsoft.com. Nie możemy zagwarantować odpowiedzi na każde otrzymane pytanie, ale staramy się jak możemy.

Jak używać bloków Try/Catch/Finally w programie Windows PowerShell?

Cześć, Skrypciarze! W programie Windows PowerShell brakuje mi możliwości używania bloków Try/Catch/Finally, dostępnego w języku C#. Jest to wygodny sposób obsługi błędów: można podjąć próbę wykonania jakiegoś działania, wychwycić powstałe błędy i posprzątać powstały bałagan. Czy istnieją plany, aby wprowadzić podobnie ustrukturyzowaną obsługę błędów w programie Windows PowerShell 3.0? Wiem, że istnieje instrukcja Trap, ale jej funkcjonalność jest niewielka w porównaniu z blokami Try/Catch/Finally.

-- JK

Cześć, JK!

Czołem, tutaj skrypciarz Ed Wilson. Dzisiaj autorzy podcastu Get-Scripting będą przeprowadzać ze mną wywiad. Mam nadzieję, że będzie ciekawie — mamy rozmawiać o zalecanych procedurach pracy z programem Windows PowerShell, a także o Igrzyskach Skrypciarskich, które rozpoczną się 26 kwietnia. Zarówno przed wywiadem, jak i po nim mam zaplanowane spotkania, tak więc teraz, czyli rankiem, mam jedyną okazję, aby sprawdzić pocztę przesłaną na adres scripter@microsoft.com i opublikować parę rzeczy w serwisach Twitter i Facebook. Reszta dnia już nie należy do mnie. Piję dosyć zwyczajną czarną herbatę, którą trochę urozmaiciłem, dodając do niej anyżek. Jest zupełnie niezła, ale raczej nie wejdzie na stałe do mojego repertuaru.

JK, nie musisz czekać na ukazanie się programu Windows PowerShell 3.0, ponieważ program Windows PowerShell 2.0, zainstalowany w systemach Windows 7 i Windows Server 2008 R2, zawiera już bloki Try/Catch/Finally.

Używając bloków Try/Catch/Finally, należy umieścić polecenie, które chcemy wykonać, w bloku Try. W razie wystąpienia błędu po wykonaniu polecenia błąd zostanie zapisany w zmiennej $Error, a wykonywanie skryptu przejdzie do bloku Catch. W skrypcie TestTryCatchFinally.ps1 polecenie Try powoduje podjęcie próby utworzenia obiektu. Ciąg znaków informuje, że skrypt podejmuje próbę utworzenia nowego obiektu. Tworzony obiekt jest przechowywany w zmiennej $ob1. Aplet polecenia New-Object tworzy obiekt. Po utworzeniu obiektu i przechowaniu go w zmiennej $a, aplet polecenia Get-Member powoduje wyświetlenie elementów członkowskich obiektu. Technikę tę ilustruje następujący kod:

Try

 {

  "Próba utworzenia nowego obiektu $ob1"

   $a = new-object $ob1

   "Elementy członkowskie obiektu $ob1"

   "Utworzono nowy obiekt $ob1"

   $a | Get-Member

 }

Następnie blok Catch pozwala przechwycić błędy, które wystąpiły w bloku Try. Można określić, jakie typy błędów mają zostać przechwycone, a także działanie, jakie ma zostać wykonane po wystąpieniu błędów. W skrypcie TestTryCatchFinally.ps1 monitoruję pojawianie się błędów System.Exception (j.ang.). Klasa System.Exception platformy .NET jest klasą bazową, z której wywodzą się wszystkie wyjątki. Oznacza to, że klasa System.Exception jest tak ogólna, jak to tylko możliwe; spowoduje t przechwycenie wszystkich wstępnie zdefiniowanych, powszechnych wyjątków środowiska wykonawczego systemu. Po przechwyceniu błędu można określić, jaki kod ma zostać wykonany. W naszym przykładzie wyświetlamy ciąg znaków z informacją o przechwyceniu wyjątku systemowego. Blok Catch widać poniżej:

Catch

 {

  [system.exception]

  "przechwycono wyjątek systemowy"

 }

Blok Finally sekwencji Try/Catch/Finally jest zawsze wykonywany — niezależnie od tego, czy wystąpił błąd. Oznacza to, że wszelkie oczyszczanie kodu, np. zwalnianie obiektów COM, należy umieszczać w bloku Finally. W skrypcie TestTryCatchFinally.ps1 blok Finally powoduje wyświetlenie ciągu znaków z informacją o zakończeniu skryptu. Widać to poniżej:

Finally

 {

  "koniec skryptu"

 }

Pełen skrypt TestTryCatchFinally.ps1 widać poniżej.

TestTryCatchFinally.ps1

$ob1 = "kenobie"

"Początek testu"



Try

 {

  "Próba utworzenia nowego obiektu $ob1"

   $a = new-object $ob1

   "Elementy członkowskie obiektu $ob1"

   "Utworzono nowy obiekt $ob1"

   $a | Get-Member

 }

Catch [system.exception]

 {

  "przechwycono wyjątek systemowy"

 }

Finally

 {

  "koniec skryptu"

 }

Jeśli skrypt TestTryCatchFinally.ps1 zostanie uruchomiony z wartością obiektu $ob1 równą „kenobie”, wystąpi błąd, ponieważ nie istnieje obiekt o nazwie „kenobie” możliwy do utworzenia przy użyciu apletu polecenia New-Object. Poniżej widać dane wyjściowe skryptu.

Jak widać na poprzednim obrazie, ciąg znaków „Początek testu” jest wyświetlany, ponieważ znajduje się on poza pętlą Try/Catch/Finally. W obrębie bloku Try ciąg znaków „Próba utworzenia nowego obiektu kenobie” jest wyświetlana, ponieważ pojawia się ona przed poleceniem New-Object. Ilustruje to fakt, że zawsze jest podejmowana próba wykonania bloku Try. Elementy członkowskie obiektu „kenobie” nie są wyświetlane, podobnie jak ciąg „Utworzono nowy obiekt kenobie”. Wskazuje to, że po wygenerowaniu błędu skrypt przechodzi do kolejnego bloku.

W bloku Catch zostaje przechwycony i wyświetlony błąd System.Exception. Wyświetlany jest również ciąg „Przechwycono wyjątek systemowy”. Następnie skrypt przechodzi do bloku Finally i zostaje wyświetlony ciąg znaków „koniec skryptu”.

Jeśli skrypt zostanie uruchomiony z wartością obiektu $ob1 równą „system.object” (jest to prawidłowy obiekt), blok Try zostanie pomyślnie wykonany. Jak widać poniżej, wyświetlane są elementy członkowskie obiektu oraz ciąg znaków informujący o pomyślnym utworzeniu obiektu. Blok Catch nie jest rozpoczynany, ale wyświetlany jest ciąg znaków „koniec skryptu”, pochodzący z bloku Finally.

Bloki Try/Catch/Finally mogą obejmować wiele bloków Catch. Należy jednak pamiętać, że w razie wystąpienia wyjątku program Windows PowerShell opuszcza blok Try i szuka bloku Catch. Zostanie użyty pierwszy blok Catch pasujący do wygenerowanego wyjątku. Dlatego też warto najpierw używać najbardziej konkretnego wyjątku, przechodząc potem do bardziej ogólnych. Widać to w skrypcie TestTryMultipleCatchFinally.ps1.

TestTryMultipleCatchFinally.ps1

$ob1 = "foo"

"Początek testu"

$ErrorActionPreference = "stop"

Try

 {

  Get-Content foo

  "Próba utworzenia nowego obiektu $ob1"

   $a = new-object $ob1

   "Elementy członkowskie obiektu $ob1"

   "Utworzono nowy obiekt $ob1"

   $a | Get-Member

 }

Catch [System.Management.Automation.PSArgumentException]

 {

  "nieprawidłowy obiekt"

 }

Catch [system.exception]

 {

  "przechwycono wyjątek systemowy"

 }

Finally

 {

  "koniec skryptu"

 }

Poniżej widać dane wyjściowe uzyskiwane po uruchomieniu skryptu TestTryMultipleCatchFinally.ps1. Wprowadzone zostały dwie zmiany: Polecenie $ErrorActionPreference jest oznaczone jako komentarz, podobnie jak polecenie Get-Content foo. Tak więc zostanie wygenerowany błąd związany z próbą utworzenia nieistniejącego obiektu. Aby odszukać konkretny błąd, sprawdziłem zmienną $error po uruchomieniu nieprawidłowego polecenia. Błąd można znaleźć w polu Exception. Błąd, który otrzymujemy, stanowi wystąpienie błędu System.Management.Automation.PSArgumentException. Widać to poniżej:

PS C:\> $error | fl * -f





PSMessageDetails      :

Exception             : System.Management.Automation.PSArgumentException: Nie można odnaleźć typu [foo]: upewnij się, że zestaw zawierający ten typ_

został załadowany. at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)

TargetObject          :

CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException

FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

ErrorDetails          :

InvocationInfo        : System.Management.Automation.InvocationInfo

PipelineIterationInfo : {0, 0}

Jeśli w skrypcie występuje kilka błędów, a preferowane działanie błędu jest ustawione na zatrzymanie, pierwszy błąd spowoduje przerwanie skryptu. Usunięcie oznaczenia komentarza z wierszy $ErrorActionPreference i Get-Content sprawia, że pierwszy wygenerowany błąd jest przechwytywany przez blok System.Exception Catch, co powoduje pominięcie wyjątku argumentu. Widać to poniżej.

I to już wszystko, co trzeba wiedzieć o używaniu bloku Try/Catch/Finally. Zapraszamy jutro na szybkie piątkowe odpowiedzi.

 Do początku strony Do początku strony

Centrum Skryptów - Systemy Operacyjne