Právě jsem narazil na problém, který mi ukazuje, že nejsem jasný v rozsahu proměnných prostředí.
Snažil jsem se použijte bundle install
, což je příkaz Ruby, který ke své práci používá hodnotu $GEM_HOME
. Nastavil jsem $GEM_HOME
, ale příkaz tuto hodnotu ignoroval, dokud jsem nepoužil export
, jako v export GEM_HOME=/some/path
.
Četl jsem, že to dělá proměnnou nějak „globální“ (také známou jako proměnná prostředí ), ale já to ne pochopit, co to znamená. Vím o globálech v programování, ale ne napříč odlišnými programy.
Také vzhledem k tomu, že moje nastavení takových proměnných platí pouze pro aktuální relaci shellu, jak bych je nastavil, řekněme, na daemonizovaný proces?
Jaké rozsahy mohou mít proměnné prostředí?
Odpověď
Procesy jsou organizovány jako strom: každý proces má jedinečného rodiče, kromě init
který PID
je vždy 1 a nemá žádného rodiče.
Vytvoření nového procesu probíhá obecně prostřednictvím dvojice fork
/ execv
systémová volání, kde prostředí podřízeného procesu je kopie nadřazeného procesu.
Chcete-li do prostředí vložit proměnnou z prostředí, musíte ji export
proměnit, aby byla rekurzivně viditelná pro všechny děti. Mějte však na paměti, že pokud dítě změní hodnotu proměnné, změněná hodnota je viditelná pouze pro ni a pro všechny procesy vytvořené po této změně (jako kopie , jako dříve řekl).
Vezměte v úvahu také to, že podřízený proces může změnit své prostředí, například jej může resetovat na výchozí hodnoty, což se pravděpodobně provádí z login
pro příklad.
Komentáře
Odpovědět
Na nejméně pod ksh
a bash
mohou mít proměnné tři obory, ne dva jak to v současné době říkají všechny zbývající odpovědi.
V kromě exportovaných proměnných (tj. prostředí) a rozsahů nevyexportovaných proměnných prostředí existuje také třetí užší pro místní proměnné funkcí.
Proměnné deklarované ve funkcích prostředí pomocí typeset
je viditelný pouze uvnitř funkcí, z nichž jsou deklarovány, a v (pod) funkcích, které jsou odtud volány.
This 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
vytvoří tento výstup:
in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] []
Jak vidíte, exportovaná proměnná se zobrazuje z prvních tří umístění, neexportované proměnné se nezobrazují mimo aktuální prostředí a lokální proměnná funkce nemá žádnou hodnotu mimo samotnou funkci. Poslední test neukazuje vůbec žádné hodnoty, je to proto, že exportované proměnné nejsou sdíleny mezi skořápkami, tj. Mohou být pouze zděděny a zděděnou hodnotu poté nemůže ovlivnit nadřazený shell.
Všimněte si, že toto druhé chování se zcela liší od chování systému Windows, kde můžete používat systémové proměnné, které jsou plně globální a sdílené všemi procesy.
Odpovědět
Jsou vymezeny procesem
Ostatní respondenti mi pomohli pochopit, že rozsah proměnné prostředí je o procesech a jejich potomci .
Když na příkazový řádek zadáte příkaz jako ls
, ve skutečnosti jste rozdvojení procesu pro spuštění programu ls
. Nový proces má jako nadřazený váš shell.
Každý proces může mít své vlastní „lokální“ proměnné, které jsou není předáno podřízeným procesům. Může také nastavit proměnné prostředí, které jsou. Pomocí export
vytvoříte proměnnou prostředí. V obou v případě, že nesouvisející procesy (partneři originálu) proměnnou neuvidí; kontrolujeme pouze to, co dítě proc eseje viz.
Předpokládejme, že máte bash shell, kterému budeme říkat A. Napište bash
, který vytvoří podřízený proces bash shell, který „zavoláme B. Cokoli, čemu jste volali export
, v A bude stále nastaveno v B.
Nyní v B řeknete FOO=b
. Stane se jedna ze dvou věcí:
- Pokud B neobdržel (od A) proměnnou prostředí s názvem
FOO
, vytvoří místní proměnnou. Děti B to nedostanou (pokud B nezavoláexport
). - Pokud B udělal obdrží (od A) proměnnou prostředí nazvanou
FOO
, ji sama upraví a jeho následně rozdvojené děti . Děti B uvidí hodnotu, kterou B. přidělil. to však A vůbec neovlivní.
Zde je rychlá ukázka .
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"
To vše vysvětluje můj původní problém: nastavil jsem GEM_HOME
do svého shellu, ale když jsem volal bundle install
, který vytvořil podřízený proces. Protože jsem nepoužil export
, podřízený proces neobdržel shell GEM_HOME
.
Un-export
Proměnnou můžete „un-exportovat“ – zabránit jejímu předání dětem – pomocí 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
Komentáře
- Když řeknete “ upraví to pro sebe a své děti “ měli byste ujasnit, že po úpravě vytvořily pouze děti uvidí upravenou hodnotu.
- @enzotib – dobrý bod. Aktualizováno.
Odpověď
Nejlepším vysvětlením exportu je toto:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
Proměnná nastavená v subshellu nebo podřízeném prostředí je viditelná pouze pro subshell, ve kterém je definován. Exportovaná proměnná je ve skutečnosti vytvořena jako proměnná prostředí. Aby bylo jasno, váš bundle install
spustí svůj vlastní shell, který $GEM_HOME
nevidí, pokud není vytvořen environment
Proměnná aka exportována.
Dokumentaci k proměnné oblasti si můžete prohlédnout zde:
http://www.tldp.org/LDP/abs/html/subshells.html
Komentáře
- Ah, proto jsem nesprávně používal výraz “ proměnná prostředí “ pro
FOO=bar
; musíte použítexport
aby to bylo jedno. Otázka odpovídajícím způsobem opravena. - Podívejte se na odkaz, který jsem přidal.
Odpověď
Podle očekávání existuje hierarchie rozsahů proměnných.
Prostředí
Nejvzdálenějším oborem je prostředí. Toto je jediný obor spravováno operačním systémem, a proto je zaručeno, že bude existovat pro každý proces. Po spuštění procesu obdrží a kopie prostředí jeho rodiče, po kterém se oba osamostatní: úprava prostředí dítěte nezmění prostředí rodiče a úprava prostředí rodiče nezmění prostředí již existujícího dítěte.
Proměnné prostředí
Mušle mají svůj vlastní pojem proměnných. To je místo, kde věci začínají být trochu matoucí.
Když přiřadíte hodnotu proměnné v prostředí a tato proměnná již v prostředí existuje, získá proměnná prostředí novou hodnotu. Pokud však proměnná ještě není v prostředí, stane se proměnnou shell . Proměnné prostředí existují pouze v rámci procesu prostředí, podobně jako proměnné Ruby existují pouze v rámci skriptu Ruby. Nikdy nejsou zděděny podřízenými procesy.
Zde vstupuje do hry klíčové slovo export
. Kopíruje proměnnou prostředí do prostředí procesu prostředí, což umožňuje dědění podřízených procesů.
Lokální proměnné
Lokální proměnné jsou proměnné prostředí definované v blocích kódu, které je obsahují. Místní proměnné deklarujete pomocí klíčového slova typeset
(přenosné) nebo local
nebo declare
(Bash ). Stejně jako ostatní proměnné prostředí nejsou místní proměnné zděděny podřízenými procesy. Nelze také exportovat místní proměnné.
FOO=bar
, nastaví hodnotu pro aktuální proces shellu. Pokud spustím program jako (bundle install
), vytvoří se podřízený proces, který ‚ nezíská přístup kFOO
. Pokud bych ale řeklexport FOO=bar
, měl by k němu přístup dětský proces (a jeho potomci) . Jeden z nich by zase mohl volatexport FOO=buzz
ke změně hodnoty pro své potomky, nebo jenFOO=buzz
ke změně hodnoty pouze pro sebe . Je to správné?