Jeg løb lige ind i et problem, der viser mig, at jeg ikke er klar over omfanget af shellvariabler.
Jeg prøvede at brug bundle install
, som er en Ruby-kommando, der bruger værdien af $GEM_HOME
til at udføre sit arbejde. Jeg havde indstillet $GEM_HOME
, men kommandoen ignorerede denne værdi, indtil jeg brugte export
, som i export GEM_HOME=/some/path
.
Jeg læste, at dette gør variablen på en eller anden måde “global” (også kendt som en miljøvariabel ), men jeg ved ikke forstå hvad det betyder. Jeg kender til globaler i programmering, men ikke på tværs af forskellige programmer.
Da min indstilling af sådanne variabler kun gælder for den aktuelle shell-session, hvordan kan jeg indstille dem til f.eks. En dæmoniseret proces?
Hvilke rækkevidde kan shellvariabler have?
Svar
Processerne er organiseret som et træ: hver proces har en unik forælder bortset fra init
som PID
er altid 1 og har ingen forælder.
Oprettelsen af en ny proces går generelt gennem et par fork
/ execv
systemopkald, hvor miljøet i barneprocessen er en kopi af den overordnede proces.
For at placere en variabel i miljøet fra skallen skal du export
denne variabel, så den er synlig rekursivt for alle børn. Men vær opmærksom på, at hvis et barn ændrer værdien af en variabel, er den ændrede værdi kun synlig for den, og alle processer oprettet efter den ændring (som en kopi som tidligere sagde).
Tag også højde for, at en underordnet proces kan ændre sit miljø, for eksempel kunne nulstille den til standardværdier, som det sandsynligvis gøres fra login
for eksempel.
Kommentarer
Svar
Kl. mindst under ksh
og bash
, kan variabler have tre rækkevidde, ikke to som alle resterende svar fortæller i øjeblikket.
I Ud over de eksporterede (dvs. miljø) variable og shell-ikke-eksporterede variable rækkevidde er der også en tredje smallere for funktionslokalvariabler.
Variabler erklæret i shell-funktioner med typeset
token er kun synlig inde i de funktioner, de erklæres i, og i (under) funktioner kaldes derfra.
Denne ksh
/ bash
kode:
# 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
producerer dette output:
in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] []
Som du kan se, vises den eksporterede variabel fra de første tre placeringer, de ikke-eksporterede variabler vises ikke uden for den aktuelle skal, og funktionen lokal variabel har ingen værdi uden for selve funktionen. Den sidste test viser slet ingen værdier, det skyldes, at eksporterede variabler ikke deles mellem skaller, dvs. de kan kun nedarves, og den arvede værdi kan ikke påvirkes bagefter af den overordnede shell.
Bemærk, at denne sidstnævnte adfærd er helt forskellig fra Windows, hvor du kan bruge systemvariabler, der er fuldt ud globale og deles af alle processer.
Svar
De er omfattet af processen
De andre svarere hjalp mig med at forstå, at shell-variabelt omfang handler om processer og deres efterkommere .
Når du skriver en kommando som ls
på kommandolinjen, er du faktisk forking en proces til at køre ls
-programmet. Den nye proces har din shell som overordnet.
Enhver proces kan have sine egne “lokale” variabler, som er ikke videregivet til underordnede processer. Det kan også indstille “miljø” -variabler, som er. Brug af export
skaber en miljøvariabel. I begge I tilfælde, vil ikke-relaterede processer (originaler fra hinanden) ikke se variablen; vi kontrollerer kun det barn, der indkøber esses se.
Antag at du har en bash shell, som vi “kalder A. Du skriver bash
, der skaber en underordnet proces bash shell, som vi “kalder B. Alt, hvad du kaldte export
på i A, vil stadig blive indstillet i B.
Nu, i B siger du FOO=b
. En af to ting vil ske:
- Hvis B ikke modtog (fra A) en miljøvariabel kaldet
FOO
, oprettes en lokal variabel. Børn af B får ikke det (medmindre B kalderexport
). - Hvis B gjorde modtager (fra A) en miljøvariabel kaldet
FOO
, den ændrer den for sig selv og dens efterfølgende forked børn . Børn af B vil se den værdi, som B tildelte. dette påvirker dog slet ikke A.
Her “er en hurtig 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"
Alt dette forklarer mit oprindelige problem: Jeg satte GEM_HOME
i min skal, men da jeg ringede bundle install
, der skabte en underordnet proces. Fordi jeg ikke havde brugt export
, modtog underordnede processen ikke shell “s GEM_HOME
.
Fjernelse af eksport
Du kan “fjerne eksport” til en variabel – forhindre, at den overføres til børn – ved hjælp af 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
Kommentarer
- Når du siger ” det vil ændre det for sig selv og dets børn ” du skal præcisere, at kun børn oprettede efter ændringen vil se den ændrede værdi.
- @enzotib – godt punkt. Opdateret.
Svar
Den bedste forklaring, jeg kan finde på eksport, er denne:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
Variablen, der er angivet i en subshell eller underordnet shell, er kun synlig for den subshell, hvor den er defineret. Den eksporterede variabel er faktisk lavet til at være en miljøvariabel. For at være klar udfører din bundle install
sin egen shell, som ikke ser $GEM_HOME
, medmindre den er lavet til en environment
variabel aka eksporteret.
Du kan se på dokumentationen til variabelt omfang her:
http://www.tldp.org/LDP/abs/html/subshells.html
Kommentarer
- Ah, så jeg var forkert at bruge udtrykket ” miljøvariabel ” til
FOO=bar
; du skal brugeexport
for at gøre det til et. Spørgsmål rettet i overensstemmelse hermed. - Se på det link, jeg har tilføjet.
Svar
Der er som forventet et hierarki af variable rækkevidde.
Miljø
Det yderste omfang er miljøet. Dette er det eneste omfang styres af operativsystemet og er derfor garanteret at eksistere for hver proces. Når en proces startes, modtager den en kopi af forældrenes miljø, hvorefter de to bliver uafhængige: ændring af barnets miljø ændrer ikke forældrenes miljø, og ændring af forældrenes miljø ændrer ikke miljøet for et allerede eksisterende barn.
Shell-variabler
Skaller har deres egen forestilling om variabler. Det er her, tingene begynder at blive lidt forvirrende.
Når du tildeler en værdi til en variabel i en shell, og den variablen allerede findes i miljøet, modtager miljøvariablen den nye værdi. Men hvis variablen ikke er i miljøet, bliver den dog en shell variabel. Shell-variabler findes kun i shell-processen, svarende til hvordan Ruby-variabler kun findes i et Ruby-script. De arves aldrig af barneprocesser.
Her er hvor export
nøgleordet kommer til spil. Det kopierer en shellvariabel til shell-processens miljø, hvilket gør det muligt for barneprocesser at arve.
Lokale variabler
Lokale variabler er shellvariabler, der er omfattet af kodeblokkene, der indeholder dem. Du erklærer lokale variabler med typeset
nøgleordet (bærbart) eller local
eller declare
(Bash ). Som andre shellvariabler arves ikke lokale variabler af underordnede processer. Lokale variabler kan heller ikke eksporteres.
FOO=bar
i skallen, indstiller værdien for den aktuelle shell-proces. Hvis jeg derefter kører et program som (bundle install
), der skaber en underordnet proces, som ikke ‘ t får adgang tilFOO
. Men hvis jeg havde sagtexport FOO=bar
, ville barneprocessen (og dens efterkommere) have adgang til den. En af dem kunne igen ringe tilexport FOO=buzz
for at ændre værdien for sine efterkommere eller bareFOO=buzz
for kun at ændre værdien for sig selv . Er det omtrent rigtigt?