jak pobrać wartość zmiennej z wyższego zakresu
praktyka
zmienne mają swoje zakresy (scopes). zakresy są niby bardzo proste… ale czy na pewno? to jak pobrać zmienną z zakresu nadrzędnego (parent scope)?
bardzo proste:
get-variable -name <varName> -scope 1
teoria
ale, że '1′? zawsze i niezmiennie '1′? jak to tak? a jak pobrać wartość zakresu w którym obecnie się znajduję?
zmienne układane są na stosie, zwanym czasem kolejką LIFO (Last-In, First-Out). [najstarsza zarejestrowana implementacja LIFO to ’pierwsi będą ostatnimi, a ostatni pierwszymi’ więc łatwo zapamiętać (; ]. element na szczycie stosu, to element 'obecny’, i zawsze ma wartość '0′. proste, ale konsekwencje są nieoczywiste. do tego dochodzi specyfika samego PS i tego, jaki próbuje być 'przyjazny’, co dodatkowo może mieć niespodziewane konsekwencje. oprócz zakresów 'numerowanych’ są zakresy 'nazwane’ i to one powodują sporo zamieszania. nie będę kopiował definicji, które można znaleźć łatwo i szybko, ale z nazwy, podstawowe zakresy to 'Global’ i 'Script’. tak więc uruchamiając dowolny skrypt, zakresy wyglądają tak:
prosty kod testowy:
function level1 { $x = 1 $x } $x = 0 level1 $x
wyświetli oczywiście '1′ a następnie '0′ – ponieważ przy każdym wyświetleniu $x wskazuje na zmienną z obecnego zakresu. obecnego, czyli '0′, co obrazuje:
mamy więc odpowiedź 'jak pobrać obecny zakres’ – jest to po prostu zakres zero. skompliqjmy ten skrypt tak, aby przetestować czy faktycznie 'parent scope’ to '1′:
function level1 { $x = 1 "here: $x" "parent from l1: {0}" -f (Get-Variable -Name x -Scope 1).Value level2 } function level2 { $x = 2 "here: $x" "parent to l2: {0}" -f (get-variable -name x -Scope 1).value "script from l2: {0}" -f ($script:x) } $x=0 Write-Host -ForegroundColor Magenta "level1:" level1 Write-Host -ForegroundColor Red "level2:" level2 write-host -ForegroundColor Green "script:" $script:x write-host -ForegroundColor Yellow 'global:' $Global:x
stos zmiennych dla tego wywołania ma teraz taką strukturę:
oczywiste? ruchome wartości dla zakresów nazwanych mogą sprawić problem i wydawać się nieintuicyjne. ale to nie koniec pułapek. ostatnia wartość [$global:x] może przyjąć albo $null albo… zero. zależnie od środowiska uruchomieniowego [możesz odpalić kod w VSC]. dodatkowo problemy może sprawić zmiana wartości zmiennej nadrzędnej… o ile odczytana zostanie zmienna z dowolnego, pierwszego zakresu, w którym istnieje, o tyle zmiana wartości będzie wykonana w tym samym zakresie. żeby to zobrazować, zmodyfiqjmy odrobinę pierwszy kod:
function level1 { $x $x = 1 } $x = 0 level1 $x
tym razem na ekranie pojawią się dwa zera. w funkcji 'level1′ żądamy wyświetlenia $x, które w tym zakresie jeszcze nie istnienie. w związq z tym wyświetlony został $x z zakresu wyżej. ale w momencie wykonania „$x = 1”, zgodnie z definicją, iż przypisanie następuje zawsze w tym samym zakresie, zmienna została zainicjowana lokalnie – a nie nadpisana pierwsza istniejąca – i to jej przypisana została wartość '1′. a więc $x wyświtlony na końcu nie uległ zmianie i wyświelone zostało '0 0′
jeśli chcemy zmienić wartość zmiennej w zakresie nadrzędnym (parent scope) kod wygląda tak:
function level1 { $x set-variable -name x -scope 1 -Value 1 } $x = 0 level1 $x
…no i warto zwrócić uwagę, że podając nazwy zmiennych dla commandletów *-variable nie dodaje się '$’.
…i może jeszcze to, że nie ma standardowo metody aby odpytać się o to, jak głęboki jest obecnie stos. jeśli masz taki scenariusz, to przyda ci się jeden z poprzednich wpisów. no dobra, podpowiem… to będzie „((get-PSCallStack).count-1)”.
eN.