Vilka omfattningar kan skalvariabler ha?

Jag stötte på ett problem som visar att jag inte är klar över skalvariablernas omfattning.

Jag försökte använd bundle install, vilket är ett Ruby-kommando som använder värdet av $GEM_HOME för att utföra sitt arbete. Jag hade ställt in $GEM_HOME, men kommandot ignorerade det värdet tills jag använde export, som i export GEM_HOME=/some/path.

Jag läste att detta på något sätt gör variabeln ”global” (även känd som en miljövariabel ), men jag vet inte förstå vad det betyder. Jag känner till globaler i programmering, men inte över olika program.

Med tanke på att min inställning av sådana variabler endast gäller den aktuella shell-sessionen, hur skulle jag ställa in dem för, till exempel, en demoniserad process?

Vilka omfattningar kan skalvariabler ha?

Svar

Processerna är organiserade som ett träd: varje process har en unik förälder, förutom init som PID är alltid 1 och har ingen förälder.

Skapandet av en ny process går generellt genom ett par fork / execv systemanrop, där miljön i barnprocessen är en kopia av den överordnade processen.

För att placera en variabel i miljön från skalet måste du export variabeln så att den är synlig rekursivt för alla barn. Men var medveten om att om ett barn ändrar värdet på en variabel, är det ändrade värdet bara synligt för det och alla processer som skapas efter den ändringen (som en kopia , som tidigare sade).

Ta också hänsyn till att en underordnad process kan ändra sin miljö, till exempel kan återställa den till standardvärden, vilket troligen görs från login för exempel.

Kommentarer

  • Ah! OK, låt ’ se om jag förstår det här. I skalet, om jag säger FOO=bar, ställer det in värdet för den aktuella skalprocessen. Om jag sedan kör ett program som (bundle install), som skapar en underprocess som inte ’ inte får tillgång till FOO. Men om jag hade sagt export FOO=bar skulle barnprocessen (och dess ättlingar) ha tillgång till den. En av dem kan i sin tur ringa export FOO=buzz för att ändra värdet för sina ättlingar, eller bara FOO=buzz för att bara ändra värdet för sig själv . Är det ungefär rätt?
  • @NathanLong Att ’ inte exakt är det: i alla moderna skal exporteras en variabel antingen (och all värdeförändring är alltså återspeglas i miljön av ättlingar) eller inte exporteras (vilket innebär att variabeln inte finns i miljön). I synnerhet om variabeln redan finns i miljön när skalet startas exporteras den.
  • Jag var lite förvirrad av meningen ” om ett barn ändra värdet på en variabel, det ändrade värdet är bara synligt för den och alla processer som skapats efter den ändringen ”. Det skulle vara mer korrekt att säga ” … synligt för det och alla dess efterkommande processer som skapats efter den ändringen ” – den andra barn till föräldraprocessen, även de som startas efter barnprocessen, påverkas inte.

Svar

Vid minst under ksh och bash, kan variabler ha tre omfattningar, inte två som alla återstående svar för närvarande säger.

I Förutom de exporterade (dvs miljö) variabla och skal oexporterade variabla omfattningar finns det också en tredje smalare för lokala lokala variabler.

Variabler deklarerade i skalfunktioner med typeset token syns bara inuti de funktioner de deklareras i och i (under) funktioner som anropas därifrån.

Denna ksh / bash kod:

# 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 

producerar denna utgång:

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

Som du kan se visas den exporterade variabeln från de tre första platserna, de icke exporterade variablerna visas inte utanför det aktuella skalet och funktionen lokal variabel har inget värde utanför själva funktionen. Det senaste testet visar inga värden alls, det beror på att exporterade variabler inte delas mellan skal, dvs. de kan bara ärvas och det ärvda värdet kan inte påverkas efteråt av det överordnade skalet.

Observera att det senare beteendet är helt annorlunda än Windows där du kan använda systemvariabler som är helt globala och delas av alla processer.

Svar

De omfattas av processen

De andra svararna hjälpte mig att förstå att skalvariabelt omfång handlar om processer och deras ättlingar .

När du skriver ett kommando som ls på kommandoraden är du faktiskt gafflar en process för att köra ls -programmet. Den nya processen har ditt skal som dess överordnade.

Varje process kan ha sina egna ”lokala” variabler, vilka är skickas inte till underordnade processer. Det kan också ställa in ”miljö” -variabler, vilka är. Med export skapas en miljövariabel. I fallet kommer orelaterade processer (original från samma sida) inte att se variabeln, vi kontrollerar bara vad barnet förvärvar esses se.

Antag att du har ett bash-skal, som vi kommer att kalla A. Du skriver bash , vilket skapar ett underordnat bash-skal, som vi ”kommer att kalla B. Allt du kallade export på i A kommer fortfarande att ställas in i B.

Nu, i B säger du FOO=b. En av två saker kommer att hända:

  • Om B inte fick (från A) en miljövariabel som heter FOO skapas en lokal variabel. Barn till B får inte det (om inte B ringer export).
  • Om B gjorde ta emot (från A) en miljövariabel kallad FOO, den modifierar den själv och dess efterföljande barn . Barn till B ser det värde som B tilldelade. detta påverkar dock inte A alls.

Här ”är en snabb 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" 

Allt detta förklarar mitt ursprungliga problem: Jag ställde GEM_HOME i mitt skal, men när jag ringde bundle install, som skapade en barnprocess. Eftersom jag inte hade använt export, fick inte barnprocessen skalet ”s GEM_HOME.

Avporterar

Du kan ”avportera” en variabel – förhindra att den överförs till barn – genom att använda 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 säger ” det kommer att ändra det för sig själv och sina barn ” du bör klargöra att endast barn skapade efter ändringen kommer att se det modifierade värdet.
  • @enzotib – bra poäng. Uppdaterad.

Svar

Den bästa förklaringen jag kan hitta om export är den här:

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

Variabeln som är inställd i ett underskal eller underskal är endast synlig för den subshell där den definieras. Den exporterade variabeln är faktiskt gjord för att vara en miljövariabel. För att vara tydlig kör ditt bundle install sitt eget skal som inte ser $GEM_HOME såvida det inte görs till en environment variabel aka exporterad.

Du kan titta på dokumentationen för variabelt omfång här:

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

Kommentarer

  • Ah, så jag var felaktig att använda termen ” miljövariabel ” för FOO=bar; du måste använda export för att göra det till. Frågan korrigeras i enlighet med detta.
  • Ta en titt på länken jag har lagt till i.

Svar

Det finns som förväntat en hierarki av variabla omfattningar.

Miljö

Det yttersta omfånget är miljön. Detta är det enda omfånget hanteras av operativsystemet och garanteras därför att det finns för varje process. När en process startas får den en kopia av föräldrarnas miljö, varefter de två blir oberoende: att ändra barnets miljö förändrar inte föräldrars miljö och att ändra föräldrars miljö förändrar inte det för ett redan existerande barn.

Skalvariabler

Skal har sin egen uppfattning om variabler. Det är här saker och ting börjar bli lite förvirrande.

När du tilldelar ett värde till en variabel i ett skal, och den variabeln redan finns i miljön, får miljövariabeln det nya värdet. Men om variabeln inte finns i miljön ändå blir den en skal -variabel. Skalvariabler finns bara inom skalprocessen, liknande hur Ruby-variabler bara finns i ett Ruby-skript. De ärvs aldrig av barnprocesser.

Här är där export nyckelordet spelar in. Det kopierar en skalvariabel till skalprocessens miljö vilket gör det möjligt för barnprocesser att ärva.

Lokala variabler

Lokala variabler är skalvariabler som omfattas av kodblocken som innehåller dem. Du förklarar lokala variabler med typeset nyckelordet (bärbart) eller local eller declare (Bash ). Liksom andra skalvariabler ärvs lokala variabler inte av underordnade processer. Lokala variabler kan inte exporteras.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *