Welke bereiken kunnen shell-variabelen hebben?

Ik kwam net een probleem tegen dat laat zien dat ik “niet duidelijk ben over de omvang van shell-variabelen.

Ik probeerde om gebruik bundle install, wat een Ruby-commando is dat de waarde van $GEM_HOME gebruikt om zijn werk te doen. Ik had $GEM_HOME, maar het commando negeerde die waarde totdat ik export gebruikte, zoals in export GEM_HOME=/some/path.

Ik heb gelezen dat dit de variabele op de een of andere manier “global” maakt (ook bekend als een omgevingsvariabele ), maar ik doe het niet begrijp wat dat betekent. Ik ken globals bij het programmeren, maar niet tussen verschillende programmas.

Ook, aangezien mijn instelling van dergelijke variabelen alleen van toepassing is op de huidige shell-sessie, hoe zou ik ze dan instellen voor, bijvoorbeeld, een gedemoniseerd proces?

Welke bereiken kunnen shell-variabelen hebben?

Antwoord

De processen zijn georganiseerd als een boomstructuur: elk proces heeft een unieke ouder, behalve init die PID is altijd 1 en heeft geen ouder.

Het aanmaken van een nieuw proces verloopt doorgaans via een paar fork / execv systeemaanroepen, waarbij de omgeving van het onderliggende proces een kopie is van het bovenliggende proces.

Om een variabele vanuit de shell in de omgeving te plaatsen, moet je die variabele export gebruiken, zodat deze recursief zichtbaar is voor alle kinderen. Maar houd er rekening mee dat als een kind de waarde van een variabele verandert, de gewijzigde waarde alleen voor hem zichtbaar is en dat alle processen na die wijziging zijn gemaakt (zijnde een kopie , zoals voorheen said).

Houd er ook rekening mee dat een kindproces zijn omgeving zou kunnen veranderen, het bijvoorbeeld zou kunnen resetten naar standaardwaarden, zoals waarschijnlijk wordt gedaan vanaf login voor voorbeeld.

Reacties

  • Ah! OK, laat ‘ kijken of ik dit begrijp. Als ik in de shell zeg FOO=bar, stelt dat de waarde in voor het huidige shell-proces. Als ik dan een programma als (bundle install) draai, wordt er een onderliggend proces aangemaakt, dat geen ‘ toegang krijgt tot FOO. Maar als ik export FOO=bar had gezegd, zou het onderliggende proces (en zijn nakomelingen) er toegang toe hebben. Een van hen zou op zijn beurt export FOO=buzz kunnen aanroepen om de waarde voor zijn nakomelingen te wijzigen, of gewoon FOO=buzz om de waarde alleen voor zichzelf te wijzigen . Klopt dat ongeveer?
  • @NathanLong Dat ‘ is het niet precies: in alle moderne shells wordt een variabele geëxporteerd (en dus is elke verandering in waarde weerspiegeld in de omgeving van afstammelingen) of niet geëxporteerd (wat betekent dat de variabele niet in de omgeving staat). In het bijzonder, als de variabele zich al in de omgeving bevindt wanneer de shell start, wordt deze geëxporteerd.
  • Ik was een beetje in de war door de zin ” als een kind verander de waarde van een variabele, de gewijzigde waarde is alleen zichtbaar voor de variabele en alle processen die daarna zijn gemaakt, veranderen “. Het zou juister zijn om te zeggen ” … zichtbaar voor hem en al zijn onderliggende processen die na die wijziging zijn gemaakt ” – de andere kinderen van het ouderproces, zelfs degenen die na het kindproces zijn gestart, worden niet beïnvloed.

Antwoord

Op minst onder ksh en bash, variabelen kunnen drie scopes, niet twee zoals alle overige antwoorden momenteel vertellen.

In Naast de geëxporteerde (dwz omgevings) variabele en shell niet-geëxporteerde variabelen scopes, is er ook een derde smallere voor functie lokale variabelen.

Variabelen gedeclareerd in shell-functies met de typeset token zijn alleen zichtbaar binnen de functies waarin ze zijn gedeclareerd en in (sub) functies die vanaf daar worden aangeroepen.

Deze 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 

produceert deze uitvoer:

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

Zoals je kunt zien, wordt de geëxporteerde variabele weergegeven vanaf de eerste drie locaties, worden de niet-geëxporteerde variabelen niet buiten de huidige shell weergegeven en heeft de functie lokale variabele geen waarde buiten de functie zelf. De laatste test toont helemaal geen waarden, dit komt omdat geëxporteerde variabelen niet worden gedeeld tussen shells, d.w.z. ze kunnen alleen worden geërfd en de geërfde waarde kan achteraf niet worden beïnvloed door de parent-shell.

Merk op dat dit laatste gedrag behoorlijk verschilt van dat van Windows, waar u systeemvariabelen kunt gebruiken die volledig globaal zijn en door alle processen worden gedeeld.

Antwoord

Ze vallen onder het proces

De andere antwoorden hebben me geholpen te begrijpen dat het bereik van shell-variabelen ongeveer processen is en hun nakomelingen .

Als je een commando zoals ls op de commandoregel typt, ben je eigenlijk een proces forken om het ls programma uit te voeren. Het nieuwe proces heeft je shell als ouder.

Elk proces kan zijn eigen “lokale” variabelen hebben, die zijn niet doorgegeven aan onderliggende processen. Het kan ook “omgevingsvariabelen” instellen, namelijk. Door export te gebruiken, wordt een omgevingsvariabele gemaakt. In beide In het geval zullen niet-gerelateerde processen (peers van het origineel) de variabele niet zien; we controleren alleen welk kind proc esses zien.

Stel dat je een bash-shell hebt, die we “A zullen noemen”. Je typt bash , die een onderliggende process bash-shell maakt, die we B zullen noemen. Alles wat je export in A hebt aangeroepen, wordt nog steeds in B ingesteld.

Nu, in B zeg je FOO=b. Een van de volgende twee dingen zal gebeuren:

  • Als B geen omgevingsvariabele heeft ontvangen (van A) met de naam FOO, zal het een lokale variabele maken. Kinderen van B krijgen het niet (tenzij B export aanroept).
  • Als B ontvangt (van A) een omgevingsvariabele genaamd FOO, wijzigt deze voor zichzelf en zijn vervolgens gevorkte kinderen . Kinderen van B zullen de waarde zien die B heeft toegewezen. heeft echter helemaal geen invloed op A.

Hier is een korte 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" 

Dit alles verklaart mijn oorspronkelijke probleem: ik zette GEM_HOME in mijn shell, maar toen ik belde bundle install, dat een onderliggend proces heeft gemaakt. Omdat ik “niet export had gebruikt, heeft het onderliggende proces de shell” s niet ontvangen GEM_HOME.

Exporteren ongedaan maken

U kunt een variabele “un-exporteren” – voorkomen dat deze wordt doorgegeven aan kinderen – door 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 

Reacties

  • Wanneer je zegt ” het zal het voor zichzelf en zijn kinderen aanpassen ” je moet duidelijk maken dat alleen kinderen na de wijziging hebben gemaakt zal de gewijzigde waarde zien.
  • @enzotib – goed punt. Bijgewerkt.

Answer

De beste uitleg die ik kan vinden over exporteren is deze:

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

De variabelenset in een subshell of child shell is alleen zichtbaar voor de subshell waarin het is gedefinieerd. De geëxporteerde variabele is eigenlijk gemaakt om een omgevingsvariabele te zijn. Dus voor alle duidelijkheid: je bundle install voert zijn eigen shell uit die “de $GEM_HOME niet ziet, tenzij het een variabele aka geëxporteerd.

U kunt de documentatie voor variabel bereik hier bekijken:

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

Reacties

  • Ah, dus ik heb de term ” omgevingsvariabele ” voor FOO=bar; je moet export om er een te maken. Vraag dienovereenkomstig gecorrigeerd.
  • Bekijk de link die ik heb toegevoegd.

Antwoord

Er is een hiërarchie van variabele bereiken, zoals verwacht.

Omgeving

Het buitenste bereik is de omgeving. Dit is het enige bereik beheerd door het besturingssysteem en is daarom gegarandeerd aanwezig voor elk proces. Wanneer een proces wordt gestart, ontvangt het een kopie van de ouderomgeving waarna de twee onafhankelijk worden: het wijzigen van de omgeving van het kind verandert de ouderomgeving niet, en het wijzigen van de omgeving van de ouder verandert die van een reeds bestaand kind niet.

Shell-variabelen

Shells hebben hun eigen notie van variabelen. Dit is waar dingen een beetje verwarrend beginnen te worden.

Als je een waarde toekent aan een variabele in een shell, en die variabele bestaat al in de omgeving, dan ontvangt de omgevingsvariabele de nieuwe waarde. Als de variabele zich echter nog niet in de omgeving bevindt, wordt het een shell -variabele. Shell-variabelen bestaan alleen binnen het shell-proces, vergelijkbaar met hoe Ruby-variabelen alleen bestaan binnen een Ruby-script. Ze worden nooit geërfd door onderliggende processen.

Hier komt het export sleutelwoord in het spel. Het kopieert een shell-variabele naar de omgeving van het shell-proces waardoor het mogelijk wordt dat onderliggende processen erven.

Lokale variabelen

Lokale variabelen zijn shell-variabelen die zich uitstrekken tot de codeblokken die ze bevatten. Je declareert lokale variabelen met het typeset trefwoord (draagbaar) of local of declare (Bash ). Net als andere shell-variabelen worden lokale variabelen niet overgeërfd door onderliggende processen. Ook kunnen lokale variabelen niet worden geëxporteerd.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *