A PowerShellben a változóknak van szabályozható láthatóságuk (scope). Az alapszabály, hogy egy adott környezetben létrehozott változók az alkörnyezetekben is láthatók. Környezet maga a PowerShell konzol, egy függvény, egy script. Ahogy ezeket egymásból meghívjuk generálódnak az al-, al-, … környezetek. A PowerShell konzol az un. „global” környezet, az összes többi, innen hívott függvény vagy szkript ennek alkörnyezete.
Nézzük az egyszerű láthatóság alapesetét:
[1] PS I:\>$a = "valami"
[2] PS I:\>function fv{$a}
[3] PS I:\>fv
valami
[1]-ben létrehoztam egy $a változót, [2]-ben egy nagyon egyszerű függvényt, ami csak annyit csinál, hogy visszaadja $a-t, majd [3]-ban meghívtam ezt az fv nevű függvényt. Az eredmény látható: a függvény „látta” $a-t, ki tudta írni a tartalmát.
Nézzünk egy kicsit bonyolultabb esetet, mi van akkor, ha a függvényemnek is van egy $a változója:
[6] PS I:\>$a = "valami"
[7] PS I:\>function fv1{$a="másvalami";$a}
[8] PS I:\>fv1
másvalami
[9] PS I:\>$a
valami
[7]-ben az fv1 függvényem definiál egy $a-t, majd ki is írja. [8]-ban meg is kaptam a „belső” $a eredményét, de ettől függetlenül [9]-ben a „global” környezetből nézve az $a továbbra is az, ami volt. Azaz a gyerek környezet nem hat vissza a szülőre.
Ha szükségünk van külön a szülő változójára is a függvényen belül, akkor ezt meg tudjuk tenni a get-variable cmdlet –scope paraméterének beállításával. A „-scope 0” jelenti a saját környezetet, „-scope 1” jelenti a szülőkörnyezetet, „-scope 2” jelentené a nagyszülőt és így tovább:
[11] PS I:\>function fv2{$a="másvalami";$a; get-variable a -scope 0}
[12] PS I:\>fv2
másvalami
Name Value
---- -----
a másvalami
[11]-ben a saját $a-t írja ki a get-variable, a következő példában pedig a szülő $a változóját:
[14] PS I:\>function fv2{$a="másvalami";$a; get-variable a -scope 1}
[15] PS I:\>fv2
másvalami
Name Value
---- -----
a valami
A gyerek környezet függvénye meg is tudja változtatni a szülő környezet változóját, ha a set‑variable cmdlet ugyanilyen –scope paraméterét megadjuk:
[17] PS I:\>function fv3{$a="másvalami";$a; (get-variable a -scope 1).value; set-variable a -scope 1 -value "változás"}
[18] PS I:\>fv3
másvalami
valami
[19] PS I:\>$a
változás
Itt a fv3 függvényben vége felé megváltoztatom a szülő $a változóját „változás”-ra.
A 0 és a legfelső környezetre némiképp egyszerűbb szintaxissal is hivatkozhatunk:
[25] PS I:\>$c=1
[26] PS I:\>function kie{$c=2; "Én c-m:$local:c";"Te c-d:$global:c"}
[27] PS I:\>kie
Én c-m:2
Te c-d:1
[28] PS I:\>function ront{$c=2; $local:c=3;$global:c=4;"Én c-m:$local:c";"Te c-d:$global:c"}
[29] PS I:\>ront
Én c-m:3
Te c-d:4
Egyébként vigyázni kell nagyon a függvényekkel! Az alábbi kis példában, ha a programozó abból a feltételezésből indul ki, hogy a $b az úgyis 0 kezdetben, így a $b=$b+5 értéke 5 lesz, akkor nagyot fog csalódni:
[21] PS I:\>$b=10
[22] PS I:\>function elront{$b=$b+5; $b}
[23] PS I:\>elront
15
[24] PS I:\>$b
10
Mi történt itt? A láthatóság miatt az elront függvényben az értékadás jobb oldalán szereplő $b az a szülő $b-je, a bal oldali $b már a függvény belső $b-je. Tehát – bár a nevük ugyanaz – tárhely szempontjából különbözőek.
Tipp
A függvények belső változóit mindig értékadás után kezdjük el használni, nehogy tévedésből a szülő környezet által definiált ugyanolyan nevű változó értékével kezdjünk el dolgozni.
Ha nem szeretnénk, hogy egy változónkhoz valamelyik gyerek környezet hozzáférhessen, akkor nyilvánítsuk privátnak ezt a változót:
[32] PS I:\>$private:priv=1
[33] PS I:\>function latom{"Privát: $priv"}
[34] PS I:\>latom
Privát:
[35] PS I:\>$priv
1
Ha egy függvénynek mégis ennek értékével kell dolgoznia, akkor természetesen paraméterátadással ez megtehető minden további nélkül, lényeg az, hogy a privát változókat a gyerek környezet nem fogja tudni a szülő tudta nélkül megváltoztatni.
Azonban azért így sem lehetünk teljes biztonságban, hiszen a get-variable és set-variable cmdletekkel még a privát változókat is elérhetjük:
[36] PS C:\> $private:priv=1
[37] PS C:\> function latom{"Privát: $((get-variable priv -scope 1).value)"}
[38] PS C:\> latom
Privát: 1
[39] PS C:\> function ront{set-variable priv -scope 1 -value 99}
[40] PS C:\> ront
[41] PS C:\> $priv
99
A fenti példában láthatjuk, hogy annak ellenére, hogy privátnak definiáltam a $priv változómat, ennek ellenére a latom függvényem is el tudta érni a get-variable segítségével, sőt, a ront függvényem még meg is tudta változtatni az értékét.
Nézzünk további lehetőségeket! Definiáltam egy scope nevű függvényt, amellyel az „aa” betűkkel kezdődő nevű, helyi változónak látszó változókat listázom ki:
[44] PS C:\> function scope {get-variable aa* -Scope local}
[45] PS C:\> scope
Ez alaphelyzetben nem ad találatot. Most definiálok egy $aa_felső változót, nézzük sajátjának tekinti-e a függvényem:
[47] PS C:\> $aa_felső = 1
[48] PS C:\> scope
Ez sem adott találatot. Most szintén a PowerShell legfelsőbb szintjén definiálok egy újabb változót, de egy speciális opció, az AllScope megadásával:
[50] PS C:\> New-Variable -Name aa_mindenütt -Value 2 -Option AllScope
[51] PS C:\> scope
Name Value
---- -----
aa_mindenütt 2
Ebből az látható, hogy a függvény is sajátjának érzi az $aa_mindenütt változót! Olyannyira, hogy meg is lehet változtatni az értékét függvényből is:
[53] PS C:\> function scope-allscope {$aa_mindenütt++}
[54] PS C:\> scope-allscope
[55] PS C:\> $aa_mindenütt
3
És természetesen ha egy függvény definiál egy ugyanilyen nevű változót, akkor az nem újként jön létre, hanem a korábban létrehozott kap új értéket:
[56] PS C:\> function scope-újmindenütt {$aa_mindenütt = 10}
[57] PS C:\> scope-újmindenütt
[58] PS C:\> $aa_mindenütt
10
Egy Private felhasználást láttunk már, de nézzük ezt egy picit részletesebben. Amit korábban mutattam, a $Private:változó formátumban azt a New-Variable cmdlettel is természetesen el lehet érni:
[26] PS C:\> New-Variable -name titkos -Value "titok" -Option private
[27] PS C:\> $titkos
titok
[28] PS C:\> function miez {$titkos}
[29] PS C:\> miez
A fenti példában a $titok változó alapban nem látható a gyerekkörnyezetként létrejövő függvényemben. Miután a TAB-kiegészítés is függvényként fut, ezért az ilyen változók neveit nem lehet kiegészíttetni sem. Viszont a Get-Variable látja a saját scope-jában:
[30] PS C:\> Get-Variable ti*
Name Value
---- -----
titkos titok
Viszont van egy másik Private beállítás is, ez nem -option, hanem –visibility:
[31] PS C:\> New-Variable -name titkos2 -Value "titok2" -Visibility private
[32] PS C:\> function miez {$titkos2}
[33] PS C:\> miez
titok2
[34] PS C:\> Get-Variable ti*
Name Value
---- -----
titkos titok
Látható, hogy az így felvett változó már láthatóság szempontjából elérhető egy al-scope-ból, de a Get‑Variable nem látja. És most jön a meglepetés:
[36] PS C:\> $titkos2
Cannot
access the variable '$titkos2' because it is a private variable
At line:1
char:9
+ $titkos2
<<<<
+ CategoryInfo : PermissionDenied: (titkos2:String)
[], SessionS
tateException
+ FullyQualifiedErrorId : VariableIsPrivate
A saját scope-ból nem szólítható meg. Ennek értelme nem a globális scope-ban van, hanem majd a következő fejezet végén nyer értelmet, és majd a modulok tárgyalásánál.