Hvilke omfang kan skallvariabler ha?

Jeg fikk nettopp et problem som viser at jeg ikke er klar over omfanget av skallvariabler.

Jeg prøvde å bruk bundle install, som er en Ruby-kommando som bruker verdien av $GEM_HOME for å utføre sitt arbeid. Jeg hadde satt $GEM_HOME, men kommandoen ignorerte den verdien til jeg brukte export, som i export GEM_HOME=/some/path.

Jeg leste at dette gjør variabelen på en eller annen måte «global» (også kjent som en miljøvariabel ), men jeg vet ikke forstå hva det betyr. Jeg vet om globaler i programmering, men ikke på tvers av forskjellige programmer.

Også, gitt at min innstilling av slike variabler bare gjelder for den nåværende shell-økten, hvordan vil jeg sette dem for for eksempel en demonisert prosess? / p>

Hvilke omfang kan shellvariabler ha?

Svar

Prosessene er organisert som et tre: hver prosess har en unik forelder, bortsett fra init som PID er alltid 1 og har ingen foreldre.

Opprettelsen av en ny prosess går generelt gjennom et par fork / execv systemanrop, der miljøet til barneprosessen er en kopi av foreldreprosessen.

For å sette en variabel i miljøet fra skallet, må du export den variabelen, slik at den er synlig rekursivt for alle barn. Men vær oppmerksom på at hvis et barn endrer verdien på en variabel, er den endrede verdien bare synlig for den, og alle prosesser som er opprettet etter den endringen (å være en kopi , som tidligere sa).

Ta også hensyn til at en underordnet prosess kan endre miljøet, for eksempel kan tilbakestille den til standardverdiene, slik det sannsynligvis gjøres fra login for eksempel.

Kommentarer

  • Ah! OK, la ‘ se om jeg forstår dette. I skallet, hvis jeg sier FOO=bar, setter det verdien for den nåværende skallprosessen. Hvis jeg da kjører et program som (bundle install), som skaper en underordnet prosess som ikke ‘ t får tilgang til FOO. Men hvis jeg hadde sagt export FOO=bar, ville barneprosessen (og dens etterkommere) ha tilgang til den. En av dem kunne i sin tur ringe export FOO=buzz for å endre verdien for sine etterkommere, eller bare FOO=buzz for å endre verdien bare for seg selv . Er det omtrent riktig?
  • @NathanLong At ‘ ikke akkurat er det: i alle moderne skjell blir en variabel enten eksportert (og altså er enhver verdiendring gjenspeiles i miljøet til etterkommere) eller ikke eksporteres (noe som betyr at variabelen ikke er i miljøet). Spesielt hvis variabelen allerede er i miljøet når skallet starter, eksporteres den.
  • Jeg var litt forvirret av setningen » hvis et barn endre verdien til en variabel, den endrede verdien er bare synlig for den og alle prosesser som er opprettet etter den endringen «. Det ville være riktigere å si » … synlig for den og alle dens etterkommende prosesser opprettet etter den endringen » – den andre barn av foreldreprosessen, selv de som er startet etter barneprosessen, blir ikke berørt.

Svar

Kl. minst under ksh og bash, kan variabler ha tre omfang, ikke to som alle gjenværende svar for tiden forteller.

I I tillegg til de eksporterte (dvs. miljø) variabelen og ikke-eksporterte shell-variabellomfang, er det også en tredje smalere for lokale variabler.

Variabler deklarert i skallfunksjoner med typeset

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 

produserer denne utgangen:

in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] [] 

Som du kan se, vises den eksporterte variabelen fra de tre første stedene, de ikke-eksporterte variablene vises ikke utenfor det aktuelle skallet, og funksjonen lokal variabel har ingen verdi utenfor selve funksjonen. Den siste testen viser ingen verdier i det hele tatt, dette er fordi eksporterte variabler ikke deles mellom skjell, dvs. de kan bare arves og den arvede verdien kan ikke påvirkes av forelderskallet etterpå.

Merk at denne sistnevnte oppførselen er ganske forskjellig fra Windows-en der du kan bruke systemvariabler som er fullstendig globale og deles av alle prosesser.

Svar

De er omfanget av prosess

De andre svarerne hjalp meg til å forstå at shell-variabelt omfang handler om prosesser og deres etterkommere .

Når du skriver inn en kommando som ls på kommandolinjen, er du faktisk forking en prosess for å kjøre ls -programmet. Den nye prosessen har skallet ditt som overordnet.

Enhver prosess kan ha sine egne «lokale» variabler, som er ikke overført til underordnede prosesser. Det kan også angi «miljø» -variabler, som er. Bruk av export skaper en miljøvariabel. I begge I tilfelle vil ikke-relaterte prosesser (jevnaldrende fra originalen) ikke se variabelen; vi kontrollerer bare hva barnet anskaffer esses se.

Anta at du har et bash-skall, som vi vil kalle A. Du skriver bash , som skaper et underordnet prosess-bash-skall, som vi «vil kalle B. Alt du kalte export på i A vil fortsatt bli satt i B.

Nå, i B sier du FOO=b. En av to ting vil skje:

  • Hvis B ikke mottok (fra A) en miljøvariabel kalt FOO, vil den opprette en lokal variabel. Barn av B får ikke det (med mindre B kaller export).
  • Hvis B gjorde motta (fra A) en miljøvariabel med navn FOO, den vil endre den for seg selv og det er senere gaffelt barn . Barn av B vil se verdien som B tildelte. dette vil imidlertid ikke påvirke A i det hele tatt.

Her «er en rask 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 mitt opprinnelige problem: Jeg satte GEM_HOME i skallet mitt, men da jeg ringte bundle install, som skapte en underordnet prosess. Fordi jeg ikke hadde brukt export, mottok ikke barneprosessen skallet «s GEM_HOME.

Ikke eksportere

Du kan «un-eksportere» en variabel – forhindre at den overføres til barn – ved å bruke 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 sier » det vil endre det for seg selv og sine barn » du bør avklare at bare barn opprettet etter endringen vil se den modifiserte verdien.
  • @enzotib – godt poeng. Oppdatert.

Svar

Den beste forklaringen jeg kan finne på eksport er denne:

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

Variabelen som er angitt i et subshell eller underskall er bare synlig for subshell der den er definert. Den eksporterte variabelen er faktisk laget for å være en miljøvariabel. For å være klar utfører bundle install sitt eget skall som ikke ser $GEM_HOME med mindre det er laget til environment variabel aka eksportert.

Du kan se på dokumentasjonen for variabelt omfang her:

http://www.tldp.org/LDP/abs/html/subshells.html

Kommentarer

  • Ah, så jeg tok feil med å bruke begrepet » miljøvariabel » for FOO=bar; du må bruke export for å gjøre det til. Spørsmål korrigert tilsvarende.
  • Ta en titt på lenken jeg har lagt til i.

Svar

Det er som forventet et hierarki av variable omfang.

Miljø

Det ytterste omfanget er miljøet. Dette er det eneste omfanget administreres av operativsystemet og vil derfor garantert eksistere for hver prosess. Når en prosess startes, mottar den en kopi av foreldrenes miljø, hvoretter de to blir uavhengige: endring av barnets miljø endrer ikke foreldrenes miljø, og endring av foreldrenes miljø endrer ikke miljøet til et allerede eksisterende barn.

Shell-variabler

Skjell har sin egen forestilling om variabler. Det er her ting begynner å bli litt forvirrende.

Når du tilordner en verdi til en variabel i et skall, og den variabelen allerede eksisterer i miljøet, mottar miljøvariabelen den nye verdien. Men hvis variabelen ikke er i miljøet, blir den en skall -variabel. Skallvariabler eksisterer bare i skallprosessen, på samme måte som Ruby-variabler bare eksisterer i et Ruby-skript. De arves aldri av barneprosesser.

Her er export nøkkelordet spiller inn. Det kopierer en skallvariabel inn i skallprosessens miljø som gjør det mulig for barneprosesser å arve.

Lokale variabler

Lokale variabler er skallvariabler som er avgrenset til kodeblokkene som inneholder dem. Du deklarerer lokale variabler med typeset søkeordet (bærbart) eller local eller declare (Bash ). Som andre skallvariabler arves ikke lokale variabler av barneprosesser. Lokale variabler kan ikke eksporteres.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *