A PowerShell programozási nyelv

Kivételkezelés

A Windows PowerShell hibakezelését sajnos nem találjuk meg az msdn-en, ugyanis egyáltalán nincs dokumentálva (2006 óta), a Windows PowerShell Blog-on található bejegyzés szerint a Windows PowerShell: TFM könyv szerzői ingyenesen elérhetővé tették a 11. fejezetet, mely ezzel a témakörrel foglalkozik, sajnos a blogon található link nem működik és a google cache-ből sem érhető el a pdf.

A PowerShell kivételkezelésének megértéséhez szükséges a .NET kivételkezelésének megértése. Szerencsére ez jól dokumentált (link1, link2), így csak egy, a legszükségesebb információkat tartalmazó gyors összefoglalás található itt.

A .NET keretrendszerben, amennyiben egy hiba történik egy metódusban, melyet a metóduson belül nem lehet megoldani, az egy kivételes esemény. Ekkor egy kivétel (Exception) dobódik (throw), mely a System.Exception osztály egy altípusa (amely tartalmazza a hibaüzenetet és a stack trace-t is). A hívó pedig dönthet úgy, hogy ezt a kivételt elkapja (catch) és lekezeli, vagy továbbdobja. További lehetőség, hogy el sem kapja, ekkor a stack-ben lévő következő hívó kapja meg a kivételt.

Jópár kivételeket reprezentáló osztály található a .NET framework-ben, a PowerShell Management és Automation névtereiben pedig még több. Azonban ezekre is igaz, hogy közös ősük a System.Exception osztály (és általában tartalmazzák a hibaüzenetet és/vagy a stack trace-t).

Kivételek kiváltása

A .NET-ben csak kivételt dobhatunk, de PowerShellben akármit. Amikor valami olyat dobunk, ami nem egy kivétel, azt becsomagolja a PowerShell egy RuntimeException-be. Például ha "throw (ls *.txt)"-t írunk PowerShellben, akkor az ls (ekvivalens a dir paranccsal) kimenete konvertálódik string-é és a RuntimeException üzenet (message) részébe kerül.

Természetesen dobhatunk szabványos kivételeket is (például: throw (new-object IO.DirectoryNotFoundException) ).

Általában érdemes a már meglévő kivétel-hierarchiából választani kivételt, nem pedig a RuntimeException-re hagyatkozni, mert így lehetőségünk adódik specifikus kivételek elkapására.

Kivételek kezelése

A Windows PowerShellben a kivételek elkapására a "trap" utasítást használhatjuk.

Fontos megjegyezni, hogy nem feltétlen szükséges a hibák kezelését megvalósítanunk. A $ErrorActionPreference-et beállíthatjuk "Continue"-ra és "SilentlyContinue"-ra is. Ez ekvivalens a Visual Basic ON ERROR RESUME NEXT-jével, ekkor nem szükséges hibakezelő részeket írnunk. Amennyiben biztosak vagyunk abban, hogy a scriptünkben hol léphetnek fel olyan hibák amiket nem kívánunk kezelni, ezen helyek előtt átállhatunk SilentlyContinue-ra (majd ezen részek után a "if(-not $?){ #Error handling with use of $Error }" elágazás segítségével megvizsgálni, volt e hiba, ha akarjuk egyáltalán ellenőrizni).

A Windows PowerShell rendelkezik több beépített változóval, melyekkel a hibák bekövetkezése esetén megjelenített információ mennyiségét szabályozhatjuk:

Összefoglalva, a PowerShell kivételkezelése hasonlít a VB kivételkezelésére (ON ERROR GOTO TRAP ... RESUME NEXT):

Elhelyezünk egy trap-et (minden egyes kezelendő kivételnek), majd pedig amikor kiváltódik a kivétel, a vezérlés a trap blokk első utasításának adódik át. A hibakezelés után vagy "continue" vagy "break" utasítást adhatunk ki. Fontos megjegyezni, hogy a PowerShellben a trap-ek sorrendje és pozíciója adott scope-on belül nem számít.

Szemben azzal, ami jópár PowerShell könyvben olvasható, a "continue" után a vezérlés a hibát kiváltó utasítás után következő utasításnak adódik át, NEM pedig a következő sornak.

Első példánk elég egyszerű:

function testTrap() {    trap [Exception] {       write-host       write-error $("TRAPPED: " + $_.Exception.GetType().FullName);       write-error $("TRAPPED: " + $_.Exception.Message);       continue;    }    write-host "Hello " -nonewline;    throw (new-object IO.DirectoryNotFoundException); write-host "World!";    write-host "Hello World!"; }

A kimenet:

Hello TRAPPED: System.IO.DirectoryNotFoundException TRAPPED: Attempted to access a path that is not on the disk. World! Hello World!

Van egy harmadik lehetőségünk is a "break" és "continue" utasítások mellett, a "return (argument)". Mivel a return működését befolyásolja a $ErrorActionPreference, ezért a működése gyakorlatilag kiszámíthatatlan, emiatt pedig kerülendő a használata. Ha a trap-en belül szeretnénk valamit kiiratni, használjuk inkább a Write-Output cmdlet-et.

A hagyományos Try/Catch/Finally

A fent bemutatott trap bevezetése nem aratott osztatlan elismerést a PowerShell felhasználók körében. Adam Weigert elkészítette a C#-ból ismerős Try/Catch/Finally kulcsszavak implementációját.

Kivételek terjedése

Ez a példa szemléleti, hogy ha break-et használunk, akkor a kivétel továbbdobódik egy külső trapnek:

Function testTrap2() {    trap [Exception] { # outer scope       write-host "Trapped $($_.Exception.Message) in the Outer Trap!"       continue    }    if($true){ # nested scope       trap [Exception] {          write-host "Trapped $($_.Exception.Message) in the Inner Trap!"          break       }       throw "Some funny message";       "We won't reach this!"    }    "Having fun with Exception-Handling !!!" }

A kimenet:

Trapped Some funny message in the Inner Trap! Trapped Some funny message in the Outer Trap! Having fun with Exception-Handling !!!