Vegyünk egy egyszerű példát: matematikai műveleteket szeretnénk elvégeztetni két paraméterként megadott számmal. Első próbálkozás:
function művelet1 {
param(
[double] $x,
[string] $művelet,
[double] $y
)
switch($művelet){
"+" {$x + $y}
"/" {$x / $y}
"*" {$x * $y}
"%" {$x % $y}
"-" {$x - $y}
}
}
Működés szempontjából nem rossz:
PS C:\> művelet1 1 + 2
3
PS C:\> művelet1 5 * 2
10
Viszont a kódban egyszerűsítésért kiáltanak az ismételt szerkezetű sorok a switch kifejezésben. Nézzük meg azt, hogy összerakjuk a teljes műveletet egy kifejezésben és hajtsuk végre azt:
function művelet2 {
param(
[double] $x,
[string] $művelet,
[double] $y
)
& "$x $művelet $y"
}
Sajnos azonban ezt futtatva hibát kapunk:
PS C:\> művelet2 1 + 2
& : The term '1 + 2' is not recognized as the name of a cmdlet, function, scri
pt file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At line:7 char:7
+ & "$x $művelet $y"
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (1 + 2:String) [], CommandNotFo
undException
+ FullyQualifiedErrorId : CommandNotFoundException
Biztonsági okokból a PowerShell sztringeket nem enged csak úgy futtatni. Az egyik megoldás, hogy az Invoke-Expression cmdletet használjuk, mint ahogy azt a 1.4.15 Végrehajtás (., &, Invoke-Expression) fejezetben láttuk:
function művelet3 {
param(
[double] $x,
[string] $művelet,
[double] $y
)
Invoke-Expression "$x $művelet $y"
}
Másik lehetőség, hogy scriptblock adattípust építünk és azt hajtjuk végre:
function művelet4 {
param(
[double] $x,
[string] $művelet,
[double] $y
)
& ([scriptblock]::Create("$x $művelet $y"))
}
A fenti példa ténylegesen nem sokban különbözik az Invoke-Expression-ös megoldástól, mindkettőben az $x és $y sztringként helyettesítődik be a kifejezésbe, majd az egész kifejezés újra kiértékelődik.
Profibb lenne, ha az $x és $y nem menne át az ide-oda alakításon, hanem értékadással kerülne be a kifejezésbe. Ehhez az utolsó megoldást tudjuk továbbfejleszteni:
function művelet5 {
param(
[double] $x,
[string] $művelet,
[double] $y
)
$sb = [scriptblock]::Create("`$args[0] $művelet `$args[1]")
&$sb $x $y
}
A szkriptblokkok használata sok egyéb előnnyel jár az Invoke-Expression-höz képest. Nézzük a következő példát:
PS C:\> $a = 1
PS C:\> $első = {$a}
PS C:\> $a = 2
PS C:\> $második = {$a}
PS C:\>
PS C:\> &$első
2
PS C:\> &$második
2
Létrehoztam egy $a változót 1 értékkel, majd egy $első változót, mely egy szkriptblokkot tartalmaz, ami csak egyszerűen kiírja az $a változót. Majd megismételtem ugyanezt, csak az $a-nak 2-őt adtam, majd a $második szkriptblokkot hoztam létre.
Látható, hogy amikor futtattam a két szkriptblokkot, akkor mindkettő 2-t adott vissza, azaz hiába hoztam létre az $első-t akkor, amikor még $a értéke 1 volt, mivel ezek a szkriptblokkok a külső környezettel szimbiózisban élnek együtt, ezért mindig az aktuális $a értékét adják vissza, ebben nincsen semmi meglepő.
Azonban ezt lehet másképp is:
PS C:\> $a = 1
PS C:\> $első = {$a}.GetNewClosure()
PS C:\> $a = 2
PS C:\> $második = {$a}.GetNewClosure()
PS C:\>
PS C:\> &$első
1
PS C:\> &$második
2
PS C:\> $a = 10
PS C:\> &$első
1
PS C:\> &$második
2
A fenti példában majdnem ugyanazt csináltam, mint korábban, azzal a különbséggel, hogy a szkriptblokkok létrehozásakor azon melegükben a GetNewClosure() metódust hívtam meg. Ezzel elszakítottam őket az őket definiáló környezettől olyan módon, hogy egy pillanatfelvétel erejéig még átadtam nekik, amire szükségük volt, jelen esetben a $a-t. De innentől kezdve akármit csinálok az $a-val, őket ez nem zavarja.
Érdekes módon, még ekkor is kapcsolatba tudunk kerülni a bennük tárolt $a-val! Bruce Payette elárulta nekünk a PowerShell in Action könyvében:
PS C:\> & $első.Module set-variable a 11
PS C:\> & $második.Module set-variable a 12
PS C:\> &$első
11
PS C:\> &$második
12
Nem mondom, hogy ezt magamtól is kitaláltam volna, illetve nem nagyon találkoztam ezzel a trükkel korábban, de jó tudni, hogy ilyen is van. Az is kiderül itt, hogy a GetNewClosure()-al létrehozott szkrtipblokk valójában egy modul, méghozzá a dinamikus fajta.
Ez mindjárt beindította a fantáziámat! Lehet, hogy ezzel a módszerrel szkriptmodulok változóiba is be tudunk nyúlni? Nézzük meg! Van egy egyszerű modulom, hangsúlyozom, hogy egy PSM1 fájlom:
$script:t = 6
function hatszorozó ($x){
$t * $x
}
Ebben ugye egy hatszorozó nevű függvényem. Import után úgy műküdik, ahogy elvárjuk:
PS C:\> Import-Module C:\PowerShell\Könyv\moduleclosure.psm1
PS C:\> hatszorozó 5
30
Vegyük elő Bruce trükkjét:
PS C:\> & (Get-Module moduleclosure) Get-Variable t -value
6
PS C:\> & (Get-Module moduleclosure) Set-Variable t -value 5
PS C:\> hatszorozó 5
25
Egyrészt ki tudtuk olvasni a modul saját változóját, másrészt át is tudtam írni és ezzel a hatszorozó függvény immáron csak ötszöröző lett.