Właśnie napotkałem problem, który pokazuje mi, że nie mam pewności co do zakresu zmiennych powłoki.
Próbowałem użyj polecenia bundle install
, które jest poleceniem Rubiego, które używa wartości $GEM_HOME
do wykonania swojej pracy. Ustawiłem $GEM_HOME
, ale polecenie ignorowało tę wartość, dopóki nie użyłem export
, jak w export GEM_HOME=/some/path
.
Czytałem, że w ten sposób zmienna jest w jakiś sposób „globalna” (znana również jako zmienna środowiskowa ), ale nie zrozumieć, co to oznacza. Wiem o zmiennych globalnych w programowaniu, ale nie w różnych programach.
Biorąc pod uwagę, że moje ustawienie takich zmiennych dotyczy tylko bieżącej sesji powłoki, jak ustawić je dla, powiedzmy, procesu demonizowanego?
Jakie zakresy mogą mieć zmienne powłoki?
Odpowiedź
Procesy są zorganizowane w postaci drzewa: każdy proces ma unikalnego rodzica, z wyjątkiem init
, które PID
jest zawsze 1 i nie ma rodzica.
Tworzenie nowego procesu odbywa się zwykle przez parę fork
/ execv
wywołania systemowe, w których środowisko procesu potomnego to kopia procesu nadrzędnego.
Aby umieścić zmienną w środowisku z poziomu powłoki, musisz export
tę zmienną, aby była widoczna rekurencyjnie dla wszystkich dzieci. Należy jednak pamiętać, że jeśli dziecko zmieni wartość zmiennej, zmieniona wartość będzie widoczna tylko dla niego i wszystkich procesów utworzonych po tej zmianie (będąc kopią , jak poprzednio powiedziane).
Weź również pod uwagę, że proces potomny może zmienić swoje środowisko, na przykład może zresetować go do wartości domyślnych, jak to prawdopodobnie zrobiono z login
dla przykład.
Komentarze
Odpowiedź
At przynajmniej poniżej ksh
i bash
, zmienne mogą mieć trzy zakresy, a nie dwa tak, jak wszystkie pozostałe odpowiedzi mówią obecnie.
W oprócz wyeksportowanych (tj. środowiskowych) zakresów zmiennych i niezaeksportowanych w powłoce zakresów zmiennych, istnieje również trzeci węższy dla zmiennych lokalnych funkcji.
Zmienne zadeklarowane w funkcjach powłoki z typeset
są widoczne tylko wewnątrz funkcji, w których są zadeklarowane, oraz w (pod) funkcjach wywoływanych stamtąd.
To ksh
/ bash
code:
# Create a shell script named /tmp/show that displays the scoped variables values. echo "echo [$environment] [$shell] [$local]" > /tmp/show chmod +x /tmp/show # Function local variable declaration function f { typeset local=three echo "in function": . /tmp/show } # Global variable declaration export environment=one # Unexported (i.e. local) variable declaration shell=two # Call the function that creates a function local variable and # display all three variable values from inside the function f # Display the three values from outside the function echo "in shell": . /tmp/show # Display the same values from a subshell echo "in subshell": /tmp/show # Display the same values from a disconnected shell (simulated here by a clean environment start) echo "in other shell" env -i /tmp/show
generuje następujące dane wyjściowe:
in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] []
Jak widać, wyeksportowana zmienna jest wyświetlana z pierwszych trzech lokalizacji, niewyeksportowane zmienne nie są wyświetlane poza bieżącą powłoką, a zmienna lokalna funkcji nie ma wartości poza samą funkcją. Ostatni test nie pokazuje żadnych wartości, ponieważ wyeksportowane zmienne nie są współdzielone między powłokami, tj. Można je tylko dziedziczyć, a powłoka nadrzędna nie może później wpłynąć na odziedziczoną wartość.
Zwróć uwagę, że to drugie zachowanie jest zupełnie inne niż w systemie Windows, w którym można używać zmiennych systemowych, które są w pełni globalne i współdzielone przez wszystkie procesy.
Odpowiedź
Są one objęte zakresem procesu
Inni odpowiadający pomogli mi zrozumieć, że zakres zmiennej powłoki obejmuje około procesów i ich potomków .
Po wpisaniu polecenia, takiego jak ls
w wierszu poleceń, w rzeczywistości tworzenie procesu w celu uruchomienia programu ls
. Nowy proces ma Twoją powłokę jako element nadrzędny.
Każdy proces może mieć własne zmienne „lokalne”, którymi są nie są przekazywane do procesów podrzędnych. Może również ustawiać zmienne „środowiskowe”, którymi są. Użycie export
tworzy zmienną środowiskową. W dowolnym przypadek, niepowiązane procesy (rówieśnicy oryginału) nie zobaczą zmiennej; kontrolujemy tylko jaki proc potomny eses, patrz.
Załóżmy, że masz powłokę bash, którą nazwiemy A. Wpisujesz bash
, który tworzy powłokę bash procesu potomnego, którą nazwiemy B. Wszystko, co wywołałeś export
w A, będzie nadal ustawione w B.
Teraz, w B mówisz FOO=b
. Stanie się jedna z dwóch rzeczy:
- Jeśli B nie otrzymał (od A) zmiennej środowiskowej o nazwie
FOO
, utworzy zmienną lokalną. Elementy potomne B go nie dostaną (chyba że B wywołaexport
). - Jeśli B zrobił otrzyma (od A) zmienną środowiskową o nazwie
FOO
, zmodyfikuje ją dla siebie i jego później rozwidlone elementy podrzędne . Elementy potomne klasy B zobaczą przypisaną wartość B. Jednak w ogóle nie wpłynie to na A.
Oto krótkie demo .
FOO=a # set "local" environment variable echo $FOO # "a" bash # forks a child process for the new shell echo $FOO # not set exit # return to original shell echo $FOO # still "a" export FOO # make FOO an environment variable bash # fork a new "child" shell echo $FOO # outputs "a" FOO=b # modifies environment (not local) variable bash # fork "grandchild" shell echo $FOO # outputs "b" exit # back to child shell exit # back to original shell echo $FOO # outputs "a"
Wszystko to wyjaśnia mój pierwotny problem: ustawiłem GEM_HOME
w mojej powłoce, ale kiedy zadzwoniłem bundle install
, który utworzył proces potomny. Ponieważ nie używałem export
, proces potomny nie otrzymał powłoki „s GEM_HOME
.
Cofnij eksportowanie
Możesz „wyeksportować” zmienną – uniemożliwić przekazywanie jej do dzieci – używając export -n FOO
.
export FOO=a # Set environment variable bash # fork a shell echo $FOO # outputs "a" export -n FOO # remove environment var for children bash # fork a shell echo $FOO # Not set exit # back up a level echo $FOO # outputs "a" - still a local variable
Komentarze
- Kiedy mówisz ” zmodyfikuje go dla siebie i swoich dzieci ” należy wyjaśnić, że tylko elementy podrzędne utworzone po modyfikacji zobaczy zmodyfikowaną wartość.
- @enzotib – słuszna uwaga. Zaktualizowano.
Odpowiedź
Najlepsze wyjaśnienie eksportowania, jakie mogę znaleźć, jest następujące:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
Zmienna ustawiona w podpowłoce lub powłoce podrzędnej jest widoczna tylko dla podpowłoka, w której jest zdefiniowana. Wyeksportowana zmienna jest faktycznie zmienną środowiskową. Aby było jasne, bundle install
wykonuje własną powłokę, która nie „widzi $GEM_HOME
, chyba że jest environment
zmienna aka wyeksportowana.
Możesz przejrzeć dokumentację dotyczącą zakresu zmiennych tutaj:
http://www.tldp.org/LDP/abs/html/subshells.html
Komentarze
- Ach, więc błędnie użyłem terminu ” zmienna środowiskowa ” dla
FOO=bar
; musisz użyćexport
aby to było. Pytanie odpowiednio poprawione. - Spójrz na link, który dodałem.
Odpowiedź
Zgodnie z oczekiwaniami istnieje hierarchia zakresów zmiennych.
Środowisko
Najbardziej zewnętrznym zakresem jest środowisko. To jest jedyny zakres zarządzany przez system operacyjny i dlatego jest gwarantowany dla każdego procesu. Gdy proces jest uruchamiany, otrzymuje plik kopia środowiska rodzica, po którym oboje stają się niezależni: modyfikacja środowiska dziecka nie zmienia środowiska rodzica, a modyfikacja środowiska rodzica nie zmienia środowiska już istniejącego dziecka.
Zmienne powłoki
Powłoki mają własne pojęcie zmiennych. W tym miejscu sytuacja zaczyna być nieco zagmatwana.
Kiedy przypisujesz wartość do zmiennej w powłoce, a ta zmienna już istnieje w środowisku, zmienna środowiskowa otrzymuje nową wartość. Jeśli jednak zmienna nie znajduje się jeszcze w środowisku, staje się zmienną powłoki . Zmienne powłoki istnieją tylko w procesie powłoki, podobnie jak zmienne Ruby istnieją tylko w skrypcie Ruby. Nigdy nie są dziedziczone przez procesy potomne.
Tutaj pojawia się słowo kluczowe export
. Kopiuje zmienną powłoki do środowiska procesu powłoki, umożliwiając procesom potomnym dziedziczenie.
Zmienne lokalne
Zmienne lokalne są zmiennymi powłoki obejmującymi bloki kodu, które je zawierają. Deklarujesz zmienne lokalne za pomocą słowa kluczowego typeset
(przenośne) lub local
lub declare
(Bash ). Podobnie jak inne zmienne powłoki, zmienne lokalne nie są dziedziczone przez procesy potomne. Nie można także eksportować zmiennych lokalnych.
FOO=bar
, ustawia to wartość dla bieżącego procesu powłoki. Jeśli następnie uruchomię program taki jak (bundle install
), który utworzy proces potomny, który nie ' nie uzyska dostępu doFOO
. Ale gdybym powiedziałexport FOO=bar
, proces potomny (i jego potomkowie) miałby dostęp do niego. Jeden z nich mógłby z kolei wywołaćexport FOO=buzz
, aby zmienić wartość dla swoich potomków lub po prostuFOO=buzz
, aby zmienić wartość tylko dla siebie . Czy to prawda?