Jakie zakresy mogą mieć zmienne powłoki?

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

  • Ach! OK, niech ' zobaczę, czy to rozumiem. W powłoce, jeśli powiem 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 do FOO. 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 prostu FOO=buzz, aby zmienić wartość tylko dla siebie . Czy to prawda?
  • @NathanLong That ' to nie jest dokładnie to: we wszystkich nowoczesnych powłokach zmienna jest albo eksportowana (więc każda zmiana wartości jest odbite w środowisku potomków) lub nieeksportowane (co oznacza, że zmienna nie znajduje się w środowisku). W szczególności, jeśli zmienna jest już w środowisku w momencie uruchomienia powłoki, jest eksportowana.
  • Byłem trochę zdezorientowany zdaniem „, jeśli dziecko zmienić wartość zmiennej, zmieniona wartość jest widoczna tylko dla niej i wszystkich procesów utworzonych po tej zmianie „. Lepiej byłoby powiedzieć ” … widoczne dla niego i wszystkich jego procesów potomnych utworzonych po tej zmianie ” – drugi elementy podrzędne procesu nadrzędnego, nawet te uruchomione po procesie potomnym, nie ulegają zmianie.

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ła export).
  • 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.

Dodaj komentarz

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