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
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 Bexport
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 moetexport
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.
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 totFOO
. Maar als ikexport FOO=bar
had gezegd, zou het onderliggende proces (en zijn nakomelingen) er toegang toe hebben. Een van hen zou op zijn beurtexport FOO=buzz
kunnen aanroepen om de waarde voor zijn nakomelingen te wijzigen, of gewoonFOO=buzz
om de waarde alleen voor zichzelf te wijzigen . Klopt dat ongeveer?