Po przeczytaniu 24.2. Zmienne lokalne , pomyślałem, że zadeklarowanie zmiennej var
za pomocą słowa kluczowego local
oznacza, że var
”wartość była dostępna tylko w bloku kodu oddzielonym nawiasami klamrowymi funkcji.
Jednak po uruchomieniu następującego przykładu okazało się, że var
można również uzyskiwać dostęp, czytać i zapisywać z funkcji wywoływanych przez ten blok kodu – tj. nawet jeśli var
jest zadeklarowane jako local
to outerFunc
, innerFunc
nadal może go odczytać i zmienić jego wartość.
#!/usr/bin/env bash function innerFunc() { var="new value" echo "innerFunc: [var:${var}]" } function outerFunc() { local var="initial value" echo "outerFunc: before innerFunc: [var:${var}]" innerFunc echo "outerFunc: after innerFunc: [var:${var}]" } echo "global: before outerFunc: [var:${var}]" outerFunc echo "global: after outerFunc: [var:${var}]"
Wyjście:
global: before outerFunc: [var:] # as expected, `var` is not accessible outside of `outerFunc` outerFunc: before innerFunc: [var:initial value] innerFunc: [var:new value] # `innerFunc` has access to `var` ?? outerFunc: after innerFunc: [var:new value] # the modification of `var` by `innerFunc` is visible to `outerFunc` ?? global: after outerFunc: [var:]
Q : Czy to błąd w mojej powłoce (bash 4.3.42, Ubuntu 16.04, 64-bitowy), czy jest to oczekiwane zachowanie?
EDYCJA: Rozwiązane. Jak zauważył @MarkPlotnick, jest to rzeczywiście oczekiwane zachowanie.
Komentarze
Odpowiedź
Zmienne powłoki mają zakres dynamiczny . Jeśli zmienna jest zadeklarowana jako lokalna dla funkcji, ten zakres pozostaje do momentu powrotu funkcji, także podczas wywołań innych funkcji.
Istnieją dwa wyjątki:
-
w ksh93, jeśli funkcja jest zdefiniowana za pomocą standardowej składni
function_name () { … }
, jej zmienne lokalne są zgodne z dynamicznym określaniem zakresu. Ale jeśli funkcja jest zdefiniowana przy użyciu składni kshfunction function_name { … }
, to jej zmienna lokalna jest zgodna z leksykalnym / statycznym zakresem, więc nie są one widoczne w innych wywołanych przez to funkcjach. -
zsh/private
automatycznie ładowana wtyczka wzsh
zapewniaprivate
słowo kluczowe / wbudowane, które może być użyte do zadeklarowania zmiennej o zakresie statycznym.
ash, bash, pdksh i pochodne, bosh mają tylko dynamiczne określanie zakresu.
Komentarze
- Czy wszystkie zmienne w powłoce mają zasięg dynamiczny, czy też dotyczy to tylko zmiennych zadeklarowanych za pomocą
local
? - @HaroldFischer Wszystkie zmienne mają zakres dynamiczny. W przypadku deklaracji
typeset
lubdeclare
lublocal
zakres obowiązuje do momentu powrotu funkcji. Bez takiej deklaracji zakres jest globalny. - IMHO,
If a variable is declared as local to a function, that scope remains until the function returns.
nie wystarczy, aby wyjaśnić, czym jest zakres dynamiczny verus leksykalny. Sam opis jest również stosowany do zakresu leksykalnego. - @jinbeomhong Nie, w przypadku zakresu leksykalnego zmienna funkcji nie jest widoczna, gdy ta funkcja wywołuje inną funkcję. ' dodałem zdanie, aby to wyraźnie stwierdzić.
- Czy ma to również wpływ na wbudowane wywołania funkcji? A może wbudowane mają swój własny zakres?
Odpowiedź
To nie jest błąd, wywołanie wewnątrz kontekstu z externalFunc korzysta z tej lokalnej kopii zmiennej $ var. „Lokalny” w externalFunc oznacza, że globalny nie został zmieniony. Jeśli wywołasz innerFunc poza externalFunc, nastąpi zmiana w globalnej $ var, ale nie w lokalnej $ var zewnętrznej funkcji. Jeśli dodasz „local” do innerFunc, to externalFunc „s $ var nie zostanie zmieniony – w istocie byłoby ich 3:
- $ global :: var
- $ externalFunc :: var
- $ innerFunc :: var
aby użyć formatu przestrzeni nazw Perla, rodzaj.
Odpowiedź
W function innerFunc()
element var="new value"
nie został zadeklarowany jako lokalny , dlatego jest dostępny w widocznym zakresie (raz funkcja została wywołana).
I odwrotnie, w function outerFunc()
local var="initial value"
został zadeklarowany jako lokalny , dlatego „nie jest dostępny w zasięgu globalnym (nawet jeśli funkcja została wywołana).
Ponieważ innerFunc()
został wywołany jako element potomny outerFunc()
, var należy do lokalnego zakresu outerFunc()
.
man 1 bash
może pomóc w wyjaśnieniu
local [opcja] [nazwa [= wartość] …]
Dla każdego argumentu lokalna tworzona jest zmienna o nazwie nazwa i przypisywana jej wartość. Opcją może być dowolna z opcji zaakceptowanych przez deklarację.Gdy funkcja local jest używana w funkcji, powoduje, że nazwa zmiennej ma widoczny zakres ograniczony do tej funkcji i jej elementów podrzędnych. …
Domniemane zachowanie, którego oczekuje się w opisie, można osiągnąć, deklarując local var="new value
w function innerFunc()
.
Jak powiedzieli inni, nie jest to błąd w powłoce bash. Wszystko działa tak, jak powinno.
Komentarze
- Twoja pierwsza wypowiedź jest sprzeczna z tym, co widzi użytkownik. Drukowanie wartości
var
w zakresie globalnym, po wywołaniuinnerFunc
dooutFunc
, powoduje nie drukujnew value
.
Odpowiedź
Możesz użyć funkcja wymuszająca zakres lokalny:
sh_local() { eval "$(set)" command eval "\"\$@\"" }
Przykład:
x() { z="new value" printf "function x, z = [%s]\n" "$z" } y() { z="initial value" printf "function y before x, z = [%s]\n" "$z" sh_local x printf "function y after x, z = [%s]\n" "$z" } printf "global before y, z = [%s]\n" "$z" y printf "global after y, z = [%s]\n" "$z"
Wynik:
global before y, z = [] function y before x, z = [initial value] function x, z = [new value] function y after x, z = [initial value] global after y, z = [initial value]
var
jest pusta?var
jest ustawiany globalnie winnerFunc
, więc dlaczego nie ' nie trzyma się do końca skryptu?