Perché non riesco a stampare una variabile che posso vedere nell'output di env?


9

Sono interessato a impostare le variabili ambientali di un'istanza shell da un'altra. Quindi ho deciso di fare qualche ricerca. Dopo aver letto un certo numero di domande circa questo ho deciso di provarlo.

Ho generato due gusci A e B (PID 420), entrambi in esecuzione zsh. Dalla shell AI è stato eseguito il seguente.

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

Dalla shell B quando corro envvedo che la variabile FOO è effettivamente impostata con un valore di bar. Questo mi fa pensare che FOO sia stato inizializzato con successo nell'ambiente della shell B. Tuttavia, se provo a stampare FOO, ottengo una riga vuota che implica che non è impostata. Per me, sembra che ci sia una contraddizione qui.

Questo è stato testato sia sul mio sistema Arch GNU / Linux che su una macchina virtuale Ubuntu. Ho anche testato questo in bashcui la variabile non si presentava nemmeno in env. Questo, sebbene deludente per me, ha senso se la shell memorizza nella cache una copia del suo ambiente al momento della generazione e la utilizza solo (come suggerito in una delle domande collegate). Questo non risponde ancora perché zshpuò vedere la variabile.

Perché l'output è echo $FOOvuoto?


MODIFICARE

Dopo l'input nei commenti ho deciso di fare un po 'più di test. I risultati possono essere visualizzati nelle tabelle seguenti. Nella prima colonna è la shell in cui è FOOstata iniettata la variabile. La prima riga contiene il comando il cui output è visibile sotto di esso. La variabile FOOè stato iniettato utilizzando: sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'. I comandi specifici di zsh: zsh -c '...'sono stati testati anche usando bash. I risultati erano identici, la loro produzione è stata omessa per brevità.

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 |

Quanto sopra sembra implicare che i risultati sono agnostici di distribuzione. Questo non mi dice molto di più zshe bashgestisce l'impostazione delle variabili in modo diverso. Inoltre, export FOOha un comportamento molto diverso in questo contesto a seconda della shell. Speriamo che questi test possano chiarire qualcosa a qualcun altro.


Cosa succede se si fa un zsh -c 'echo $FOO'(utilizzare virgolette singole!) Invece? Riesci a vederlo allora?
user1934428

Il valore corretto viene stampato da una nuova sotto-shell (testata anche per il bambino bash). Chiaramente l'ambiente è in qualche modo persistente in quanto il bambino può ereditarlo, ma perché il genitore non lo onora?
rlf,

3
È quello che pensavo. Immagino che la shell abbia da qualche parte una tabella dei simboli di variabili, alcune delle quali sono contrassegnate come "esportate", il che significa che all'apertura di una subshell vengono inserite nell'ambiente del processo figlio. Inizialmente (all'avvio della shell), le variabili dall'ambiente in quel momento vengono copiate nella tabella dei simboli (ovviamente anche come variabili "esportate"). Quando si modifica l'ambiente, la shell non viene notata per aggiornare la tabella dei simboli, ma i processi figlio (come env) vedono l'ambiente modificato.
user1934428,

2
Ho provato su Ubuntu 16.04 con zsh 5.1.1 e bash 4.3.48 (1) e sembra che l'impostazione di una variabile di ambiente per zshin GDB non la renda visibile come variabile di shell ma la faccia passare ai processi figlio (come hai osservato), mentre impostarne uno per bash lo rende visibile come variabile di shell ma non lo fa passare ai processi figlio! Sembra che zsh e bash utilizzino strategie diverse per la gestione delle variabili, con zsh che tiene traccia delle variabili non ambientali e che bash memorizza tutto nel suo ambiente che sanifica quando avvia un figlio (non subshell).
Eliah Kagan,

@EliahKagan, interessante; dovresti postarlo come risposta. Mi chiedo anche se fa la differenza se si esegue export FOOin bash?
Wildcard il

Risposte:


2

La maggior parte delle shell non utilizza l' API getenv()/ setenv()/ putenv().

All'avvio, creano variabili shell per ciascuna variabile d'ambiente. Saranno archiviati in strutture interne che devono trasportare altre informazioni come se la variabile sia esportata, di sola lettura ... Non possono usare le libc environper quello.

Allo stesso modo, e per questo motivo, non useranno execlp(), execvp()per eseguire comandi ma execve()chiamare direttamente la chiamata di sistema, calcolando l' envp[]array in base all'elenco delle loro variabili esportate.

Quindi, nel tuo gdb, avresti bisogno di aggiungere una voce a quella tabella di variabili interna delle shell, o possibilmente chiamare la giusta funzione che gli farebbe interpretare un export VAR=valuecodice per aggiornare quella tabella da sola.

Quanto al motivo per cui si vede una differenza tra bashe zshquando si chiama setenv()in gdb, ho il sospetto che sia perché si sta chiamando setenv()prima dei inizializza shell, per esempio entrando main().

Noterai bashche main()è int main(int argc, char* argv[], char* envp[])(e bashmappa le variabili da quelle aree in envp[]) mentre zshè int main(int argc, char* argv[])e zshottiene invece le variabili environ. setenv()modifica environma non può essere modificato envp[]sul posto (sola lettura su diversi sistemi e sulle stringhe a cui puntano questi puntatori).

In ogni caso, dopo che la shell ha letto environall'avvio, l'utilizzo setenv()sarebbe inefficace in quanto la shell non utilizza più environ(o getenv()) in seguito.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.