Was ist der Unterschied in der Verwendung zwischen Shell-Variablen und Umgebungsvariablen?

16

Ich wusste eigentlich nicht, dass es zwei verschiedene Arten von Variablen gibt, auf die ich über die Befehlszeile zugreifen kann. Ich wusste nur, dass ich Variablen deklarieren kann wie:

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

oder mit einem $ -Zeichen darauf zugreifen, wie:

echo $foo
echo ${bar[1]}

oder mit eingebauten Variablen, wie:

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

Jetzt höre ich, dass es zwei (mindestens?) Arten von Variablen gibt: Shell-Variablen und Umgebungsvariablen.

  • Was ist der Zweck von zwei verschiedenen Typen?
  • Woher weiß ich, welcher Typ eine Variable ist?
  • Was sind die typischen Verwendungen für jeden?
Hai
quelle

Antworten:

14

Umgebungsvariablen sind eine Liste von name=valuePaaren, die unabhängig vom Programm existieren (Shell, Anwendung, Daemon…). Sie werden normalerweise von untergeordneten Prozessen geerbt (erstellt von fork/ execsequence): Untergeordnete Prozesse erhalten eine eigene Kopie der übergeordneten Variablen.

Shell-Variablen existieren nur im Kontext einer Shell. Sie werden nur in Subshells vererbt (dh wenn die Shell ohne execOperation gegabelt wird ). Abhängig von den Shell-Funktionen können Variablen nicht nur einfache Zeichenfolgen wie Umgebungszeichenfolgen sein, sondern auch Arrays, zusammengesetzte, typisierte Variablen wie Ganzzahlen oder Gleitkommazahlen usw.

Wenn eine Shell gestartet wird, werden alle von ihr IFSgeerbten Umgebungsvariablen zu Shellvariablen (es sei denn, sie sind als Shellvariablen und andere Eckfälle, wie sie von einigen Shells zurückgesetzt werden, ungültig ). Diese geerbten Variablen werden jedoch als exportiert markiert 1 . Dies bedeutet, dass sie für untergeordnete Prozesse mit dem potenziell aktualisierten Wert, der von der Shell festgelegt wurde, verfügbar bleiben. Dies gilt auch für Variablen, die unter der Shell erstellt und mit dem exportSchlüsselwort exportiert wurden .

Array- und andere Variablen mit komplexem Typ können nur exportiert werden, wenn ihr Name und Wert in das name=valueMuster konvertiert werden können oder wenn ein Shell-spezifischer Mechanismus vorhanden ist (z. B .: bashExportiert Funktionen in die Umgebung und einige exotische, nicht POSIX-Shell-ähnliche rcund esexportierbare Arrays) ).

Der Hauptunterschied zwischen Umgebungsvariablen und Shell-Variablen besteht also in ihrem Gültigkeitsbereich: Umgebungsvariablen sind global, während nicht exportierte Shell-Variablen für das Skript lokal sind.

Beachten Sie auch, dass moderne Shells (zumindest kshund bash) einen dritten Shell-Variablenbereich unterstützen. Variablen, die in Funktionen mit dem typesetSchlüsselwort erstellt wurden, sind für diese Funktion lokal (Die Art und Weise, wie die Funktion deklariert wird, aktiviert / deaktiviert diese Funktion ksh, und das Persistenzverhalten unterscheidet sich zwischen bashund ksh). Siehe /unix//a/28349/2594

1 Dies gilt für moderne Shells wie ksh, dash, bashund ähnliche. Die alten Bourne-Shell- und Nicht-Bourne-Syntax-Shells cshhaben unterschiedliche Verhaltensweisen.

jlliagre
quelle
1
Alles wird von untergeordneten Prozessen geerbt, da untergeordnete Prozesse als Abzweigung (exakte Kopie) der übergeordneten Prozesse erstellt werden. Der Punkt bei Umgebungsvariablen ist, dass sie an den execve()Systemaufruf übergeben werden, damit (normalerweise) Daten während der Ausführung anderer Befehle (im selben Prozess) erhalten bleiben .
Stéphane Chazelas
Nicht alle Umgebungsvariablen werden in Shell-Variablen übersetzt. Nur diejenigen, die als Shell-Variablenname gültig sind (und mit wenigen Ausnahmen wie IFSin einigen Shells).
Stéphane Chazelas
Shells mögen rc, eskönnen Arrays exportieren , um eine Ad - hoc - Codierung. bashund rckann Funktionen auch mithilfe von Umgebungsvariablen exportieren (wiederum mithilfe einer speziellen Codierung).
Stéphane Chazelas
In ksh93, typesetschränkt nur den Umfang in Funktionen mit der erklärten function foo { ...; }Syntax, nicht mit der Bourne ( foo() cmd) Syntax (und es ist statisch Scoping nicht dynamisch wie in anderen Shells).
Stéphane Chazelas
@ StéphaneChazelas Vielen Dank für die Überprüfung! Die Antwort wurde aktualisiert, um Ihre Anmerkungen zu berücksichtigen.
Juli
17

Shell-Variablen

Shell-Variablen sind Variablen, deren Gültigkeitsbereich sich in der aktuellen Shell-Sitzung befindet, beispielsweise in einer interaktiven Shell-Sitzung oder einem Skript.

Sie können eine Shell-Variable erstellen, indem Sie einem nicht verwendeten Namen einen Wert zuweisen:

var="hello"

Die Verwendung von Shell-Variablen dient dazu, die Daten in der aktuellen Sitzung zu verfolgen. Shell-Variablen haben normalerweise Namen mit Kleinbuchstaben.

Umgebungsvariablen

Eine Umgebungsvariable ist eine Shell-Variable, die exportiert wurde. Dies bedeutet, dass es als Variable nicht nur in der Shell-Sitzung angezeigt wird, die es erstellt hat, sondern auch für alle Prozesse (nicht nur Shells), die von dieser Sitzung aus gestartet werden.

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

oder

export VAR="hello"

Sobald eine Shell-Variable exportiert wurde, bleibt sie exportiert, bis sie nicht mehr gesetzt ist oder bis ihre "export-Eigenschaft" entfernt wird (mit export -nin bash), sodass sie normalerweise nicht erneut exportiert werden muss. Wenn Sie eine Variable mit deaktivieren, wird sie gelöscht unset(unabhängig davon, ob es sich um eine Umgebungsvariable handelt oder nicht).

Arrays und assoziative Hashes in bashund andere Shells können möglicherweise nicht als Umgebungsvariablen exportiert werden. Umgebungsvariablen müssen einfache Variablen sein, deren Werte Zeichenfolgen sind und deren Namen häufig aus Großbuchstaben bestehen.

Die Verwendung von Umgebungsvariablen dient dazu, die Daten in der aktuellen Shell-Sitzung zu verfolgen, aber auch jedem gestarteten Prozess zu ermöglichen, an diesen Daten teilzunehmen. Der typische Fall hierfür ist die PATHUmgebungsvariable, die in der Shell festgelegt und später von jedem Programm verwendet werden kann, das Programme starten möchte, ohne einen vollständigen Pfad zu ihnen anzugeben.

Die Sammlung von Umgebungsvariablen in einem Prozess wird häufig als "die Umgebung des Prozesses" bezeichnet. Jeder Prozess hat eine eigene Umgebung.

Umgebungsvariablen können nur "weitergeleitet" werden, dh ein untergeordneter Prozess kann die Umgebungsvariablen in seinem übergeordneten Prozess niemals ändern, und abgesehen vom Einrichten der Umgebung für einen untergeordneten Prozess beim Starten ändert ein übergeordneter Prozess möglicherweise nicht die vorhandene Umgebung von a untergeordneter Prozess.

Umgebungsvariablen können mit env(ohne Argumente) aufgelistet werden . Ansonsten sehen sie in einer Shell-Sitzung genauso aus wie nicht exportierte Shell-Variablen. Dies ist etwas speziell für die Shell, da die meisten anderen Programmiersprachen normalerweise keine "normalen" Variablen mit Umgebungsvariablen vermischen (siehe unten).

env kann auch verwendet werden, um die Werte einer oder mehrerer Umgebungsvariablen in der Umgebung eines Prozesses festzulegen, ohne sie in der aktuellen Sitzung festzulegen:

env CC=clang CXX=clang++ make

Dies beginnt makedamit, dass die Umgebungsvariable CCauf den Wert gesetzt clangund auf CXXgesetzt wird clang++.

Es kann auch verwendet werden, um die Umgebung für einen Prozess zu löschen :

env -i bash

Dies beginnt bashaber nicht die aktuelle Umgebung auf den neuen übertragen bashProzess (es wird nach wie vor hat Umgebungsvariablen , wie sie neue aus seiner Schale Initialisierungsskripts erstellt).

Beispiel für den Unterschied

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

Andere Sprachen

In den meisten Programmiersprachen gibt es Bibliotheksfunktionen, mit denen Umgebungsvariablen abgerufen und festgelegt werden können. Da Umgebungsvariablen als einfache Schlüssel-Wert-Beziehung gespeichert werden, handelt es sich normalerweise nicht um "Variablen" der Sprache. Ein Programm kann den Wert (der immer eine Zeichenfolge ist) abrufen, der einem Schlüssel (dem Namen der Umgebungsvariablen) entspricht, muss ihn dann jedoch in eine Ganzzahl oder in den Datentyp konvertieren, den die Sprache für den Wert erwartet.

In C, kann Umgebungsvariablen zugegriffen mit getenv(), setenv(), putenv()und unsetenv(). Mit diesen Routinen erstellte Variablen werden von jedem Prozess, den das C-Programm startet, auf dieselbe Weise vererbt.

Andere Sprachen können spezielle Datenstrukturen haben, um dasselbe zu erreichen, wie der %ENVHash in Perl oder das ENVIRONassoziative Array in den meisten Implementierungen von awk.

Kusalananda
quelle
thx, brillant klare erklärung. Die Umgebung ist also wie ein großes Feld, in dem andere Programme alle Umgebungsvariablen leben und sehen können. Einige Programme haben ihre privaten Variablen, nur sie selbst können sie sehen, wie die Shell. Es gibt jedoch einen Mechanismus, mit dem private Variablen von allen "export" genannt werden. Wenn dies in Ordnung ist, ist das einzige, bei dem ich nicht sicher bin, ob es mehr als eine Umgebung gleichzeitig geben kann?
Hai
@sharkant Jeder laufende Prozess hat eine eigene Umgebung. Diese Umgebung wird von dem Prozess geerbt, der sie gestartet hat. Es gibt kein "Übersprechen" zwischen Umgebungen unterschiedlicher Prozesse. Die einzige Möglichkeit, eine Umgebungsvariable in einem Prozess zu ändern, besteht darin, dass der Prozess sie selbst ändert.
Kusalananda
Danke, dass du mein Verständnis verdeutlicht hast. Jeder Fisch in seiner eigenen Fischschale. Wie wäre es mit Prozessen, die andere Prozesse hervorbringen? Sind Prozesse und ihre untergeordneten Prozesse alle in einer Umgebung oder haben sie jeweils eine eigene?
Hai
1
@sharkant In den meisten Sprachen gibt es Bibliotheksfunktionen, mit denen Umgebungsvariablen abgerufen und festgelegt werden können. In C, geschieht dies mit getenv(), setenv(), putenv()und unsetenv(). Mit diesen Routinen erstellte Variablen werden von jedem Prozess, den das C-Programm startet, auf dieselbe Weise vererbt. Andere Sprachen haben möglicherweise spezielle Datenstrukturen für dasselbe wie %ENVin Perl.
Kusalananda
1
FWIW: Die exec*()Funktionsfamilie kann auch die Umgebung für den ausgeführten Prozess festlegen.
Satō Katsura
5

Shell-Variablen sind schwer zu duplizieren.

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

Umgebungsvariablen können jedoch dupliziert werden. Sie sind nur eine Liste, und eine Liste kann doppelte Einträge enthalten. Hier ist envdup.cgenau das zu tun.

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

Was wir kompilieren und ausführen können, envdupum dann auszuführen env, um uns zu zeigen, welche Umgebungsvariablen gesetzt sind ...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

Dies ist möglicherweise nur nützlich, um Fehler oder andere Unregelmäßigkeiten bei der Handhabung von Programmen zu finden **environ.

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

Es sieht so aus, als würde Python 3.6 hier die Duplikate blind weitergeben (eine undichte Abstraktion), Perl 5.24 hingegen nicht. Wie wäre es mit den Muscheln?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

Meine Güte, was passiert, wenn sudonur der erste Umgebungseintrag bereinigt wird und dann bashder zweite ausgeführt wird? Hallo PATHoder LD_RUN_PATHausnutzen. Ist Ihr sudo(und alles andere ?) Für dieses Loch geflickt ? Sicherheits-Exploits sind weder ein "anekdotischer Unterschied" noch ein "Fehler" im aufrufenden Programm.

Thrig
quelle
1
Das ist wahr, aber ein anekdotischer Unterschied und wahrscheinlich ein Fehler des Programms, das die doppelte Variable einstellt.
Juli
1
Siehe rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Stéphane Chazelas,
0

Eine Umgebungsvariable ähnelt einer Shell-Variablen , ist jedoch nicht spezifisch für die Shell . Alle Prozesse auf Unix-Systemen verfügen über einen Speicher für Umgebungsvariablen . Der Hauptunterschied zwischen Umgebungsvariablen und Shell-Variablen besteht darin, dass das Betriebssystem alle Umgebungsvariablen Ihrer Shell an die von der Shell ausgeführten Programme übergibt , während in den von Ihnen ausgeführten Befehlen nicht auf Shell-Variablen zugegriffen werden kann.

env –Mit dem Befehl können Sie ein anderes Programm in einer benutzerdefinierten Umgebung ausführen, ohne das aktuelle zu ändern. Bei Verwendung ohne Argument wird eine Liste der aktuellen Umgebungsvariablen gedruckt. printenv –Der Befehl druckt alle oder die angegebenen Umgebungsvariablen. set –Der Befehl setzt oder deaktiviert Shell-Variablen. Bei Verwendung ohne Argument wird eine Liste aller Variablen gedruckt, einschließlich Umgebungs- und Shell-Variablen sowie Shell-Funktionen. unset –Der Befehl löscht Shell- und Umgebungsvariablen. export –Der Befehl legt Umgebungsvariablen fest

Mehdi Sellami
quelle