Zakres zmiennych lokalnych w funkcjach powłoki

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ść.

Uruchom w trybie online

#!/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

  • Jest to oczekiwane zachowanie
  • Czy ja jedyny, który tak uważa ', jest dziwny, że w ostatnim wierszu danych wyjściowych wartość var jest pusta? var jest ustawiany globalnie w innerFunc, więc dlaczego nie ' nie trzyma się do końca skryptu?

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:

  1. 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 ksh function 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.

  2. zsh/private automatycznie ładowana wtyczka w zsh zapewnia private 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 lub declare lub local 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łaniu innerFunc do outFunc, powoduje nie drukuj new 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] 

Źródło

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *