Warum kann ich keine Variable drucken, die ich in der Ausgabe von env sehen kann?

9

Ich bin daran interessiert, Umgebungsvariablen einer Shell-Instanz von einer anderen zu setzen. Also habe ich beschlossen, etwas zu recherchieren. Nach der Lektüre eine Reihe von Fragen über das ich beschlossen , es zu testen.

Ich habe zwei Muscheln A und B (PID 420) hervorgebracht, die beide laufen zsh. Von der Shell AI lief folgendes.

sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach

Aus Shell B kann ich beim Ausführen envsehen, dass die Variable FOO tatsächlich mit dem Wert bar gesetzt ist. Dies lässt mich denken, dass FOO in der Umgebung von Shell B erfolgreich initialisiert wurde. Wenn ich jedoch versuche, FOO zu drucken, erhalte ich eine leere Zeile, was bedeutet, dass es nicht gesetzt ist. Für mich scheint es hier einen Widerspruch zu geben.

Dies wurde sowohl auf meinem eigenen Arch GNU / Linux-System als auch auf einer Ubuntu-VM getestet. Ich habe dies auch dort getestet, bashwo die Variable nicht einmal in env angezeigt wurde. Dies ist zwar für mich enttäuschend, aber sinnvoll, wenn die Shell beim Spawn eine Kopie ihrer Umgebung zwischenspeichert und nur diese verwendet (was in einer der verknüpften Fragen vorgeschlagen wurde). Dies antwortet immer noch nicht, warum zshdie Variable angezeigt werden kann.

Warum ist die Ausgabe von echo $FOOleer?


BEARBEITEN

Nach der Eingabe in den Kommentaren habe ich beschlossen, ein bisschen mehr zu testen. Die Ergebnisse sind in den folgenden Tabellen aufgeführt. In der ersten Spalte befindet sich die Shell, in die die FOOVariable eingefügt wurde. Die erste Zeile enthält den Befehl, dessen Ausgabe darunter angezeigt wird. Die Variable FOOwurde injiziert mit : sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'. Die für zsh: spezifischen Befehle zsh -c '...'wurden ebenfalls mit bash getestet. Die Ergebnisse waren identisch, ihre Ausgabe wurde der Kürze halber weggelassen.

Arch GNU / Linux, zsh 5.3.1, bash 4.4.12 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ubuntu 16.04.2 LTS, zsh 5.1.1, bash 4.3.48 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Das Obige scheint zu implizieren, dass die Ergebnisse verteilungsunabhängig sind. Dies sagt mir nicht viel mehr als zshund bashbehandelt die Einstellung von Variablen anders. Darüber hinaus export FOOhat sich in diesem Zusammenhang je nach Shell ein sehr unterschiedliches Verhalten. Hoffentlich können diese Tests jemand anderem etwas klar machen.

rlf
quelle
Was passiert, wenn Sie zsh -c 'echo $FOO'stattdessen ein (verwenden Sie einfache Anführungszeichen!)? Kannst du es dann sehen?
user1934428
Der korrekte Wert wird aus einer neuen Sub-Shell gedruckt (auch auf Bash Child getestet). Natürlich ist die Umgebung irgendwie hartnäckig, da das Kind sie erben kann, aber warum ehrt die Eltern sie nicht?
rlf
3
Das ist was ich dachte. Ich denke, die Shell hat irgendwo eine Symboltabelle mit Variablen, von denen einige als "exportiert" markiert sind, was bedeutet, dass sie beim Öffnen einer Subshell in die Umgebung des untergeordneten Prozesses gestellt werden. Zu Beginn (wenn die Shell startet) werden die Variablen aus der Umgebung zu diesem Zeitpunkt in die Symboltabelle kopiert (natürlich auch als "exportierte" Variablen). Wenn Sie die Umgebung ändern, wird die Shell nicht bemerkt, dass sie ihre Symboltabelle aktualisiert. Untergeordnete Prozesse (wie env) sehen jedoch die geänderte Umgebung.
user1934428
2
Ich habe unter Ubuntu 16.04 mit zsh 5.1.1 und bash 4.3.48 (1) getestet und es scheint, dass das Festlegen einer Umgebungsvariablen für zshin GDB sie nicht als Shell-Variable sichtbar macht, sondern dazu führt, dass sie an untergeordnete Prozesse (as) weitergegeben wird Sie haben beobachtet), während das Festlegen von eins für es als Shell-Variable sichtbar bash macht , aber nicht dazu führt, dass es an untergeordnete Prozesse weitergegeben wird! Es sieht so aus, als ob zsh und bash unterschiedliche Strategien zum Verwalten von Variablen verwenden, wobei zsh Nicht-Umgebungsvariablen verfolgt und bash alles in seiner Umgebung speichert, was beim Starten eines (Nicht-Subshell-) Kindes bereinigt wird.
Eliah Kagan
@EliahKagan, interessant; Sie sollten das als Antwort posten. Ich frage mich auch , ob es einen Unterschied macht , wenn Sie laufen export FOOin bash?
Wildcard

Antworten:

2

Die meisten Schalen verwenden Sie nicht die getenv()/ setenv()/ putenv()API.

Beim Start erstellen sie Shell-Variablen für jede Umgebungsvariable. Diese werden in internen Strukturen gespeichert, die andere Informationen enthalten müssen, z. B. ob die Variable exportiert wird, schreibgeschützt ... Sie können die libc's environdafür nicht verwenden .

Auf ähnliche Weise und aus diesem Grund werden sie nicht verwenden execlp(), execvp()um Befehle auszuführen , aber der Anruf execve()Systemaufruf direkt, Berechnen des envp[]Array basierend auf der Liste ihrer exportierten Variablen.

In Ihrem gdbmüssen Sie also einen Eintrag zu der internen Variablentabelle der Shells hinzufügen oder möglicherweise die richtige Funktion aufrufen, mit der ein export VAR=valueCode interpretiert wird , mit dem diese Tabelle selbst aktualisiert werden kann.

Wie, warum Sie sehen einen Unterschied zwischen bashund , zshwenn Sie anrufen setenv()in gdbvermute ich , das ist , weil Sie anrufen , setenv()bevor der Shell initialisiert, zum Beispiel beim Betreten main().

Sie werden feststellen, dass bash's main()ist int main(int argc, char* argv[], char* envp[])(und bashVariablen aus diesen env-Variablen zuordnet envp[]), während zsh' s ist int main(int argc, char* argv[])und stattdessen zshdie Variablen environabruft. setenv()ändert sich environ, kann aber nicht direkt geändert werden envp[](schreibgeschützt auf mehreren Systemen sowie die Zeichenfolgen, auf die diese Zeiger verweisen).

In jedem Fall wäre die environVerwendung nach dem Lesen der Shell beim Start setenv()unwirksam, da die Shell danach nicht mehr verwendet environ(oder getenv()).

Stéphane Chazelas
quelle