Quali ambiti possono avere le variabili shell?


42

Ho appena riscontrato un problema che mi mostra che non sono chiaro sull'ambito delle variabili della shell.

Stavo cercando di usare bundle install, che è un comando Ruby che usa il valore di $GEM_HOMEper fare il suo lavoro. Avevo impostato $GEM_HOME, ma il comando ha ignorato quel valore fino a quando non ho usato export, come in export GEM_HOME=/some/path.

Ho letto che questo rende la variabile in qualche modo "globale" (nota anche come variabile d'ambiente ), ma non capisco cosa significhi. Conosco a livello globale nella programmazione, ma non attraverso programmi distinti.

Inoltre, dato che la mia impostazione di tali variabili si applica solo alla sessione di shell corrente, come potrei impostarle per un processo demone?

Quali ambiti possono avere le variabili shell?

Risposte:


33

I processi sono organizzati come un albero: ogni processo ha un genitore unico, a parte il initquale PIDè sempre 1 e non ha un genitore.

La creazione di un nuovo processo passa generalmente attraverso una coppia di chiamate fork/ execvsystem, in cui l'ambiente del processo figlio è una copia del processo padre.

Per inserire una variabile nell'ambiente dalla shell devi exportquella variabile, in modo che sia visibile in modo ricorsivo a tutti i bambini. Tuttavia, tieni presente che se un bambino modifica il valore di una variabile, il valore modificato è visibile solo ad essa e tutti i processi creati dopo tale modifica (essendo una copia , come detto in precedenza).

Prendi anche in considerazione il fatto che un processo figlio potrebbe cambiare il suo ambiente, ad esempio potrebbe ripristinarlo ai valori predefiniti, come probabilmente è fatto loginper esempio.


1
Ah! OK, vediamo se lo capisco. Nella shell, se lo dico FOO=bar, questo imposta il valore per l'attuale processo della shell. Se quindi eseguo un programma come ( bundle install), ciò crea un processo figlio, al quale non ho accesso FOO. Ma se avessi detto export FOO=bar, il processo figlio (e i suoi discendenti) avrebbero avuto accesso ad esso. Uno di loro potrebbe, a sua volta, chiamare export FOO=buzzper cambiare il valore per i suoi discendenti, o semplicemente FOO=buzzper cambiare il valore solo per se stesso. È giusto?
Nathan Long,

2
@NathanLong Non è esattamente così: in tutte le shell moderne, una variabile viene esportata (e quindi ogni cambiamento di valore si riflette nell'ambiente dei discendenti) o non esportato (nel senso che la variabile non è nell'ambiente). In particolare, se la variabile è già nell'ambiente all'avvio della shell, viene esportata.
Gilles 'SO- smetti di essere malvagio' il

2
Ero un po 'confuso dalla frase "se un bambino cambia il valore di una variabile, il valore modificato è visibile solo ad essa e tutti i processi creati dopo quella modifica". Sarebbe più corretto dire "... visibile ad esso e a tutti i suoi processi discendenti creati dopo quel cambiamento" - gli altri figli del processo genitore, anche quelli avviati dopo il processo figlio, non sono interessati.
Jaan,

26

Almeno sotto kshe bash, le variabili possono avere tre ambiti, non due come tutte le risposte rimanenti stanno dicendo attualmente.

Oltre alla variabile esportata (es. Ambiente) e shell ambiti di variabili non esportati, ce n'è anche una terza più stretta per le variabili locali di funzione.

Le variabili dichiarate nelle funzioni shell con il typesettoken sono visibili solo all'interno delle funzioni in cui sono dichiarate e nelle (sotto) funzioni chiamate da lì.

Questo ksh/ bashcodice:

# 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 

produce questo output:

in function:
[one] [two] [three]
in shell:
[one] [two] []
in subshell:
[one] [] []
in other shell
[] [] []

Come puoi vedere, la variabile esportata viene visualizzata dalle prime tre posizioni, le variabili non esportate non vengono visualizzate al di fuori della shell corrente e la variabile locale della funzione non ha alcun valore al di fuori della funzione stessa. L'ultimo test non mostra alcun valore, questo perché le variabili esportate non sono condivise tra le shell, cioè possono essere ereditate solo e la shell madre non può influenzare il valore ereditato in seguito.

Si noti che quest'ultimo comportamento è abbastanza diverso da quello di Windows in cui è possibile utilizzare le variabili di sistema che sono completamente globali e condivise da tutti i processi.


12

Sono definiti dal processo

Gli altri risponditori mi hanno aiutato a capire che l'ambito delle variabili della shell riguarda i processi e i loro discendenti .

Quando si digita un comando come lsnella riga di comando, in realtà si sta eseguendo un fork di un processo per eseguire il lsprogramma. Il nuovo processo ha la shell come genitore.

Ogni processo può avere le sue variabili "locali", che non vengono passate ai processi figlio. Può anche impostare variabili "ambiente", che sono. L'uso exportcrea una variabile d'ambiente. In entrambi i casi, i processi non correlati (peer dell'originale) non vedranno la variabile; controlliamo solo ciò che i processi figli vedono.

Supponiamo di avere una shell bash, che chiameremo A. Digiti bash, che crea una shell bash di processo figlio, che chiameremo B. Tutto ciò che hai chiamato exportin A verrà comunque impostato in B.

Ora, in B, dici FOO=b. Accadrà una delle due cose:

  • Se B non ha ricevuto (da A) una variabile d'ambiente chiamata FOO, creerà una variabile locale. I bambini di B non lo riceveranno (a meno che B non chiami export).
  • Se B ha ricevuto (da A) una variabile d'ambiente chiamata FOO, la modificherà per sé e per i suoi figli successivamente biforcati . I figli di B vedranno il valore assegnato a B. Tuttavia, ciò non influirà affatto su A.

Ecco una breve 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'

Tutto questo spiega il mio problema originale: ho impostato GEM_HOMEnella mia shell, ma quando ho chiamato bundle install, che ha creato un processo figlio. Poiché non avevo usato export, il processo figlio non ha ricevuto la shell GEM_HOME.

Un-esportazione

Puoi "annullare l'esportazione" di una variabile, impedendone il passaggio ai bambini, utilizzando 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

1
Quando dici "lo modificherà per sé e per i suoi figli" dovresti chiarire che solo i bambini creati dopo la modifica vedranno il valore modificato.
enzotib,

1
@enzotib - buon punto. Aggiornato.
Nathan Long,

3

La migliore spiegazione che posso trovare sull'esportazione è questa:

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

La variabile impostata all'interno di una subshell o di una shell figlio è visibile solo alla subshell in cui è definita. La variabile esportata viene effettivamente creata come variabile di ambiente. Quindi, per essere chiari, si bundle installesegue la propria shell che non vede il, a $GEM_HOMEmeno che non venga creata una environmentvariabile aka esportata.

Puoi dare un'occhiata alla documentazione per ambito variabile qui:

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


Ah, quindi non ero corretto usare il termine "variabile d'ambiente" per FOO=bar; devi usarlo exportper renderlo uno. Domanda corretta di conseguenza.
Nathan Long,

Dai un'occhiata al link che ho aggiunto.
Karlson,

3

Esiste una gerarchia di ambiti variabili, come previsto.

Ambiente

L'ambito più esterno è l'ambiente. Questo è l'unico ambito gestito dal sistema operativo ed è quindi garantito che esista per ogni processo. Quando un processo viene avviato, riceve una copia dell'ambiente del suo genitore dopo il quale i due diventano indipendenti: la modifica dell'ambiente del figlio non cambia quella del genitore e la modifica dell'ambiente del genitore non cambia quella di un figlio già esistente.

Variabili della shell

Le conchiglie hanno la loro nozione di variabili. Questo è dove le cose iniziano a diventare un po 'confuse.

Quando si assegna un valore a una variabile in una shell e quella variabile esiste già nell'ambiente, la variabile di ambiente riceve il nuovo valore. Tuttavia, se la variabile non è ancora nell'ambiente, diventa una variabile shell . Le variabili shell esistono solo all'interno del processo shell, in modo simile al modo in cui le variabili Ruby esistono solo all'interno di uno script Ruby. Non sono mai ereditati da processi figlio.

Ecco dove exportentra in gioco la parola chiave. Copia una variabile di shell nell'ambiente del processo di shell rendendo possibile l'ereditarietà dei processi figlio.

Variabili locali

Le variabili locali sono variabili della shell mirate ai blocchi di codice che le contengono. Dichiarare le variabili locali con la typesetparola chiave (portatile) o localo declare(Bash). Come altre variabili shell, le variabili locali non sono ereditate dai processi figlio. Inoltre, le variabili locali non possono essere esportate.

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.