Ich bin gerade auf ein Problem gestoßen, das mir zeigt, dass mir der Umfang der Shell-Variablen nicht klar ist.
Ich habe es versucht Verwenden Sie bundle install
, einen Ruby-Befehl, der den Wert von $GEM_HOME
verwendet, um seine Arbeit zu erledigen. Ich hatte , aber der Befehl ignorierte diesen Wert, bis ich export
wie in export GEM_HOME=/some/path
verwendete.
Ich habe gelesen, dass dies die Variable irgendwie „global“ macht (auch bekannt als Umgebungsvariable ), aber ich tue es nicht verstehe was das bedeutet. Ich kenne mich mit Globals in der Programmierung aus, aber nicht mit verschiedenen Programmen.
Da meine Einstellung solcher Variablen nur für die aktuelle Shell-Sitzung gilt, wie würde ich sie beispielsweise für einen daemonisierten Prozess festlegen?
Welche Bereiche können Shell-Variablen haben?
Antwort
Die Prozesse sind als Baum organisiert: Jeder Prozess hat ein eindeutiges übergeordnetes Element, abgesehen von init
, das PID
ist immer 1 und hat kein übergeordnetes Element.
Die Erstellung eines neuen Prozesses erfolgt im Allgemeinen über ein Paar von fork
/ execv
Systemaufrufe, bei denen die Umgebung des untergeordneten Prozesses eine Kopie des übergeordneten Prozesses ist.
Um eine Variable aus der Shell in die Umgebung einzufügen, müssen Sie export
diese Variable verwenden, damit sie für alle untergeordneten Elemente rekursiv sichtbar ist. Beachten Sie jedoch, dass, wenn ein untergeordnetes Element den Wert einer Variablen ändert, der geänderte Wert nur für diese sichtbar ist und alle Prozesse nach dieser Änderung erstellt werden (wie zuvor eine Kopie ) sagte).
Berücksichtigen Sie auch, dass ein untergeordneter Prozess seine Umgebung ändern könnte, beispielsweise auf Standardwerte zurücksetzen könnte, wie dies wahrscheinlich ab login
für der Fall ist Beispiel.
Kommentare
- Ah! OK, lassen Sie ‚ sehen, ob ich das verstehe. Wenn ich in der Shell
FOO=bar
sage, wird der Wert für den aktuellen Shell-Prozess festgelegt. Wenn ich dann ein Programm wie (bundle install
) ausführe, wird ein untergeordneter Prozess erstellt, der ‚ keinen Zugriff aufFOO
. Wenn ich jedochexport FOO=bar
gesagt hätte, hätte der untergeordnete Prozess (und seine Nachkommen) Zugriff darauf. Einer von ihnen könnte wiederumexport FOO=buzz
aufrufen, um den Wert für seine Nachkommen zu ändern, oder einfachFOO=buzz
, um den Wert nur für sich selbst zu ändern . Ist das ungefähr richtig? - @NathanLong Das ‚ ist nicht genau das: In allen modernen Shells wird eine Variable entweder exportiert (und jede Wertänderung auch in der Umgebung von Nachkommen reflektiert) oder nicht exportiert (was bedeutet, dass sich die Variable nicht in der Umgebung befindet). Insbesondere wenn sich die Variable beim Start der Shell bereits in der Umgebung befindet, wird sie exportiert.
- Ich war ein wenig verwirrt über den Satz „, wenn ein Kind Wenn Sie den Wert einer Variablen ändern, ist der geänderte Wert nur für sie sichtbar, und alle nach dieser Änderung erstellten Prozesse ändern „. Es wäre richtiger zu sagen, dass “ … für ihn sichtbar ist und alle nach dieser Änderung erstellten Nachkommenprozesse “ – der andere Kinder des übergeordneten Prozesses, auch diejenigen, die nach dem untergeordneten Prozess gestartet wurden, sind nicht betroffen.
Antwort
At Zumindest unter ksh
und bash
können Variablen drei Bereiche, nicht zwei , wie alle verbleibenden Antworten derzeit aussagen.
In Zusätzlich zu den Bereichen für exportierte (dh Umgebungsvariablen) und nicht exportierte Shell-Variablen gibt es einen dritten engeren Bereich für lokale Funktionsvariablen.
In Shell-Funktionen deklarierte Variablen mit der typeset
-Token sind nur innerhalb der Funktionen sichtbar, in denen sie deklariert sind, und in (Unter-) Funktionen, die von dort aufgerufen werden.
Diese 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
erzeugt diese Ausgabe:
in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] []
Wie Sie sehen können, wird die exportierte Variable an den ersten drei Stellen angezeigt, die nicht exportierten Variablen werden nicht außerhalb der aktuellen Shell angezeigt und die lokale Variable der Funktion hat außerhalb der Funktion selbst keinen Wert. Der letzte Test zeigt überhaupt keine Werte an, da exportierte Variablen nicht von Shells gemeinsam genutzt werden, d. H. Sie können nur vererbt werden und der geerbte Wert kann danach nicht mehr von der übergeordneten Shell beeinflusst werden.
Beachten Sie, dass sich dieses letztere Verhalten erheblich von dem unter Windows unterscheidet, bei dem Sie Systemvariablen verwenden können, die vollständig global sind und von allen Prozessen gemeinsam genutzt werden.
Antwort
Sie sind nach Prozessen geordnet
Die anderen Antwortenden haben mir geholfen zu verstehen, dass es sich bei dem Bereich der Shell-Variablen um -Prozesse handelt und ihre Nachkommen .
Wenn Sie einen Befehl wie ls
in die Befehlszeile eingeben, sind Sie tatsächlich Forken eines Prozesses zum Ausführen des Programms ls
. Der neue Prozess hat Ihre Shell als übergeordnetes Element.
Jeder Prozess kann seine eigenen „lokalen“ Variablen haben Wird nicht an untergeordnete Prozesse übergeben. Es können auch „Umgebungs“ -Variablen festgelegt werden. Mit export
wird eine Umgebungsvariable erstellt. In beiden In diesem Fall sehen nicht verwandte Prozesse (Peers des Originals) die Variable nicht. Wir steuern nur, welcher untergeordnete Prozess ausgeführt wird Esses siehe.
Angenommen, Sie haben eine Bash-Shell, die wir „A“ nennen. Sie geben bash
ein , die eine untergeordnete Prozess-Bash-Shell erstellt, die wir „B“ nennen. Alles, was Sie in A export
on genannt haben, wird weiterhin in B gesetzt.
In B sagen Sie FOO=b
. Eines von zwei Dingen wird passieren:
- Wenn B (von A) keine Umgebungsvariable mit dem Namen
FOO
empfangen hat, wird eine lokale Variable erstellt. Kinder von B werden es nicht bekommen (es sei denn, B ruftexport
auf). - Wenn B empfängt (von A) eine Umgebungsvariable mit dem Namen
FOO
, ändert sie für sich selbst und seine anschließend gegabelten Kinder . Kinder von B sehen den von B zugewiesenen Wert. wirkt sich jedoch überhaupt nicht auf A aus.
Hier ist eine schnelle 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"
All dies erklärt mein ursprüngliches Problem: Ich habe GEM_HOME
in meiner Shell festgelegt, aber als ich anrief bundle install
, das einen untergeordneten Prozess erstellt hat. Da ich export
nicht verwendet hatte, hat der untergeordnete Prozess die Shell „s GEM_HOME
.
Export nicht exportieren
Sie können den Export einer Variablen „aufheben“ – verhindern, dass sie an untergeordnete Elemente übergeben wird -, indem Sie 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
Kommentare
- Wenn Sie sagen “ Es wird für sich und seine untergeordneten Elemente geändert. “ Sie sollten klarstellen, dass nur untergeordnete Elemente nach der Änderung erstellt wurden wird den geänderten Wert sehen.
- @enzotib – guter Punkt. Aktualisiert.
Antwort
Die beste Erklärung, die ich zum Exportieren finden kann, ist folgende:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
Die in einer Subshell oder untergeordneten Shell festgelegte Variable ist nur für sichtbar die Unterschale, in der es definiert ist. Die exportierte Variable wird tatsächlich als Umgebungsvariable erstellt. Um klar zu sein, führt Ihre bundle install
eine eigene Shell aus, die die $GEM_HOME
nur dann anzeigt, wenn sie als Variable aka exportiert.
Die Dokumentation zum Variablenbereich finden Sie hier:
http://www.tldp.org/LDP/abs/html/subshells.html
Kommentare
- Ah, also habe ich den Begriff “ Umgebungsvariable “ für
FOO=bar
; Sie müssenexport
um es zu einer zu machen. Frage entsprechend korrigiert. - Sehen Sie sich den Link an, den ich hinzugefügt habe.
Antwort
Es gibt erwartungsgemäß eine Hierarchie variabler Bereiche.
Umgebung
Der äußerste Bereich ist die Umgebung. Dies ist der einzige Bereich Wird vom Betriebssystem verwaltet und ist daher für jeden Prozess garantiert vorhanden. Wenn ein Prozess gestartet wird, erhält er eine Kopie der Umgebung des Elternteils, nach der die beiden unabhängig werden: Durch Ändern der Umgebung des Kindes wird die Umgebung des Elternteils nicht geändert, und durch Ändern der Umgebung des Elternteils wird die eines bereits vorhandenen Kindes nicht geändert.
Shell-Variablen
Shells haben ihre eigene Vorstellung von Variablen. Hier wird es etwas verwirrend.
Wenn Sie einer Variablen in einer Shell einen Wert zuweisen und diese Variable bereits in der Umgebung vorhanden ist, erhält die Umgebungsvariable den neuen Wert. Befindet sich die Variable jedoch noch nicht in der Umgebung, wird sie zu einer Shell -Variablen. Shell-Variablen existieren nur innerhalb des Shell-Prozesses, ähnlich wie Ruby-Variablen nur in einem Ruby-Skript existieren. Sie werden niemals von untergeordneten Prozessen geerbt.
Hier kommt das Schlüsselwort export
ins Spiel. Es kopiert eine Shell-Variable in die Umgebung des Shell-Prozesses, sodass untergeordnete Prozesse erben können.
Lokale Variablen
Lokale Variablen sind Shell-Variablen, die auf die sie enthaltenden Codeblöcke beschränkt sind. Sie deklarieren lokale Variablen mit dem Schlüsselwort typeset
(portabel) oder local
oder declare
(Bash) ). Wie andere Shell-Variablen werden lokale Variablen nicht von untergeordneten Prozessen geerbt. Auch lokale Variablen können nicht exportiert werden.