Welche Bereiche können Shell-Variablen haben?

42

Ich bin gerade auf ein Problem gestoßen, das mir zeigt, dass mir der Umfang der Shell-Variablen nicht klar ist.

Ich habe versucht bundle install, einen Ruby-Befehl zu verwenden, der den Wert von verwendet $GEM_HOME, um seine Arbeit zu erledigen. Ich hatte festgelegt $GEM_HOME, aber der Befehl ignorierte diesen Wert, bis ich exportwie in verwendet habe export GEM_HOME=/some/path.

Ich habe gelesen, dass dies die Variable irgendwie "global" macht (auch als Umgebungsvariable bekannt ), aber ich verstehe nicht, was das bedeutet. Ich kenne globale Programme, aber nicht programmübergreifend.

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?

Nathan Long
quelle

Antworten:

33

Die Prozesse sind in Form eines Baums organisiert: Jeder Prozess verfügt über ein eindeutiges übergeordnetes Element, abgesehen davon, initdass PIDes sich immer um 1 handelt und kein übergeordnetes Element vorhanden ist.

Das Erstellen eines neuen Prozesses erfolgt im Allgemeinen über ein Paar von fork/ execvsystem-Aufrufen, wobei die Umgebung des untergeordneten Prozesses eine Kopie des übergeordneten Prozesses ist.

Um eine Variable aus der Shell in die Umgebung zu setzen, müssen Sie exportdiese Variable verwenden, damit sie rekursiv für alle untergeordneten Elemente sichtbar ist. Beachten Sie jedoch, dass, wenn ein Kind den Wert einer Variablen ändert, der geänderte Wert nur für dieses Kind sichtbar ist und alle Prozesse, die nach dieser Änderung erstellt wurden (wie bereits erwähnt, eine Kopie ).

Berücksichtigen Sie auch, dass ein untergeordneter Prozess seine Umgebung ändern und beispielsweise auf die Standardwerte zurücksetzen kann, wie dies wahrscheinlich loginbeispielsweise in diesem Fall der Fall ist .

Enzotib
quelle
1
Ah! OK, mal sehen, ob ich das verstehe. Wenn ich in der Shell sage FOO=bar, legt dies den Wert für den aktuellen Shell-Prozess fest. Wenn ich dann ein Programm wie ( bundle install) ausführe , wird ein untergeordneter Prozess erstellt, auf den nicht zugegriffen werden kann FOO. Aber wenn ich gesagt hatte export FOO=bar, das Kind Prozess (und deren Nachkommen) würde den Zugang zu ihm haben. Einer von ihnen könnte wiederum anrufen export FOO=buzz, um den Wert für seine Nachkommen FOO=buzzzu ändern , oder nur , um den Wert nur für sich selbst zu ändern. Ist das ungefähr richtig?
Nathan Long
2
@NathanLong Das ist nicht genau das: In allen modernen Shells wird eine Variable entweder exportiert (und daher wird jede Wertänderung in der Umgebung von Nachkommen widergespiegelt) oder nicht exportiert (was bedeutet, dass sich die Variable nicht in der Umgebung befindet). Befindet sich die Variable beim Start der Shell bereits in der Umgebung, wird sie exportiert.
Gilles 'SO- hör auf böse zu sein'
2
Ich war ein wenig verwirrt über den Satz "Wenn ein Kind den Wert einer Variablen ändert, ist der geänderte Wert nur für sie und alle nach dieser Änderung erstellten Prozesse sichtbar". Richtiger wäre zu sagen: "... sichtbar für ihn und alle nachgeordneten Prozesse, die nach dieser Änderung erstellt wurden" - die anderen untergeordneten Prozesse des übergeordneten Prozesses, auch diejenigen, die nach dem untergeordneten Prozess gestartet wurden, sind nicht betroffen.
Jaan
26

Zumindest unter kshund bashkönnen Variablen drei Bereiche haben, nicht zwei, wie alle verbleibenden Antworten aktuell aussagen.

Zusätzlich zu den Bereichen für exportierte (dh Umgebungs) Variablen und nicht exportierte Shell-Variablen gibt es auch einen dritten engeren Bereich für lokale Funktionsvariablen.

Variablen, die in Shell-Funktionen mit dem typesetToken deklariert wurden, sind nur in den Funktionen sichtbar, in denen sie deklariert wurden, und in (Unter-) Funktionen, die von dort aufgerufen wurden.

Dieser ksh/ bashCode:

# 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, wird die exportierte Variable an den ersten drei Positionen angezeigt, die nicht exportierten Variablen werden nicht außerhalb der aktuellen Shell angezeigt und die lokale Variable der Funktion hat keinen Wert außerhalb der Funktion. Der letzte Test zeigt überhaupt keine Werte an, da exportierte Variablen nicht zwischen Shells geteilt werden, dh sie können nur geerbt werden und der geerbte Wert kann danach nicht von der übergeordneten Shell beeinflusst werden.

Beachten Sie, dass sich das letztgenannte Verhalten stark von dem von Windows unterscheidet, in dem Sie Systemvariablen verwenden können, die vollständig global sind und von allen Prozessen gemeinsam genutzt werden.

jlliagre
quelle
12

Sie sind prozessbezogen

Die anderen Antworten haben mir geholfen zu verstehen, dass es bei dem Shell-Variablenbereich um Prozesse und deren Nachkommen geht .

Wenn Sie einen Befehl wie lsin der Befehlszeile eingeben, veranlassen Sie einen Prozess, das lsProgramm auszuführen . Der neue Prozess hat Ihre Shell als übergeordnetes Element.

Jeder Prozess kann seine eigenen "lokalen" Variablen haben, die nicht an untergeordnete Prozesse übergeben werden. Es können auch Umgebungsvariablen gesetzt werden. Mit exportwird eine Umgebungsvariable erstellt. In beiden Fällen wird die Variable für nicht verwandte Prozesse (Peers des Originals) nicht angezeigt. Wir steuern nur, was untergeordnete Prozesse sehen.

Angenommen, Sie haben eine Bash-Shell, die wir A nennen. Sie geben ein bash, wodurch eine untergeordnete Prozess-Bash-Shell erstellt wird, die wir B nennen. Alles, was Sie exportin A aufgerufen haben, wird weiterhin in B gesetzt.

Nun, in B, sagst du FOO=b. Eines von zwei Dingen wird passieren:

  • Wenn B keine aufgerufene Umgebungsvariable (von A) erhalten hat FOO, wird eine lokale Variable erstellt. Kinder von B werden es nicht bekommen (außer B ruft an export).
  • Wenn B haben (von A) erhalten eine Umgebungsvariable callled FOO, wird sie es für sich selbst verändern und ihre anschließend gegabelt Kinder . Kindern von B wird der von B zugewiesene Wert angezeigt. Dies hat jedoch keinerlei Auswirkungen auf A.

Hier ist eine kurze 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_HOMEin meiner Shell festgelegt, aber als ich anrief bundle install, wurde ein untergeordneter Prozess erstellt. Da ich nicht verwendet hatte export, hat der untergeordnete Prozess die Shell nicht erhalten GEM_HOME.

Export wird abgebrochen

Sie können eine Variable "dekomprimieren" - verhindern, dass sie an untergeordnete Variablen übergeben wird -, indem Sie verwenden 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
Nathan Long
quelle
1
Wenn Sie "es wird es für sich und seine Kinder ändern" sagen, sollten Sie klarstellen, dass nur Kinder, die nach der Änderung erstellt werden, den geänderten Wert sehen.
Enzotib
1
@enzotib - guter Punkt. Aktualisiert.
Nathan Long
3

Die beste Erklärung für den Export ist die folgende:

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

Die in einer Subshell oder Child-Shell festgelegte Variable ist nur für die Subshell sichtbar, in der sie definiert ist. Die exportierte Variable ist eigentlich eine Umgebungsvariable. Um klar zu sein, bundle installführt Ihr eine eigene Shell aus, die die nicht sieht, $GEM_HOMEes sei denn, es wird eine environmentVariable namens exportiert.

Die Dokumentation zum Variablenumfang finden Sie hier:

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

Karlson
quelle
Ah, also war es falsch, den Begriff "Umgebungsvariable" für zu verwenden FOO=bar; du musst es benutzen export, um eins zu machen. Frage entsprechend korrigiert.
Nathan Long
Werfen Sie einen Blick auf den Link, den ich hinzugefügt habe.
Karlson
3

Wie erwartet gibt es eine Hierarchie variabler Bereiche.

Umgebung

Der äußerste Bereich ist die Umwelt. Dies ist der einzige Bereich, der vom Betriebssystem verwaltet wird und daher für jeden Prozess garantiert vorhanden ist. Wenn ein Prozess gestartet wird, erhält er eine Kopie der Umgebung seines Elternteils, wonach beide 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 zur Shell- Variablen. Shell-Variablen existieren nur innerhalb des Shell-Prozesses, ähnlich wie Ruby-Variablen nur innerhalb eines Ruby-Skripts existieren. Sie werden niemals von untergeordneten Prozessen geerbt.

Hier kommt das exportSchlüsselwort 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 sich auf die sie enthaltenden Codeblöcke beziehen. Sie deklarieren lokale Variablen mit dem typesetSchlüsselwort (portable) oder localoder declare(Bash). Wie andere Shell-Variablen werden lokale Variablen nicht an untergeordnete Prozesse vererbt. Auch lokale Variablen können nicht exportiert werden.

Schlange
quelle