Considera questo snippet:
$ SOMEVAR=AAA $ echo zzz $SOMEVAR zzz zzz AAA zzz
Qui ho impostato $SOMEVAR
su AAA
sulla prima riga – e quando lo faccio eco sulla seconda riga, ottengo i contenuti AAA
come previsto.
Ma poi, se provo a specificare la variabile sulla stessa riga di comando echo
:
$ SOMEVAR=BBB echo zzz $SOMEVAR zzz zzz AAA zzz
… Non ricevo BBB
come mi aspettavo, ottengo il vecchio valore ( AAA
).
È così che dovrebbero essere le cose? Se è così, come LD_PRELOAD=/... program args ...
allora puoi specificare variabili come LD_PRELOAD=/... program args ...
e farlo funzionare? Cosa mi manca?
Quello che vedi è il comportamento atteso. Il problema è che la shell padre valuta $SOMEVAR
sulla riga di comando prima di richiamare il comando con l’ambiente modificato. È necessario ottenere la valutazione di $SOMEVAR
posticipata fino a quando non viene impostato l’ambiente.
Le tue opzioni immediate includono:
SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
. SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'
. Entrambi usano virgolette singole per impedire alla shell madre di valutare $SOMEVAR
; viene valutato solo dopo che è stato impostato nell’ambiente (temporaneamente, per la durata del singolo comando).
Un’altra opzione è usare la notazione sub-shell (come suggerito anche da Marcus Kuhn nella sua risposta ):
(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)
La variabile è impostata solo nella sub-shell
Francamente, il manuale è fonte di confusione su questo punto. Il manuale di GNU Bash dice:
L’ambiente per qualsiasi semplice comando o funzione [nota che ciò esclude i builtin] può essere aumentato temporaneamente anteponendolo con assegnazioni di parametri, come descritto in Parametri Shell. Queste istruzioni di assegnazione riguardano solo l’ambiente visto da quel comando.
Se si analizza realmente la frase, ciò che sta dicendo è che l’ ambiente per il comando / funzione è stato modificato, ma non l’ambiente per il processo genitore. Quindi, questo funzionerà:
$ TESTVAR=bbb env | fgrep TESTVAR TESTVAR=bbb
perché l’ambiente per il comando env è stato modificato prima dell’esecuzione. Tuttavia, questo non funzionerà:
$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc + TESTVAR=bbb + echo aaa ccc aaa ccc
a causa di quando l’espansione dei parametri viene eseguita dalla shell.
Un’altra parte del problema è che Bash definisce questi passaggi per il suo interprete:
Quello che sta accadendo qui è che i builtin non hanno il loro ambiente di esecuzione, quindi non vedono mai l’ambiente modificato. Inoltre, i comandi semplici (ad esempio / bin / echo) ottengono un ambiente modificato (motivo per cui l’esempio env ha funzionato) ma l’espansione della shell si sta verificando nell’ambiente corrente al punto 4.
In altre parole, non si passa ‘aaa $ TESTVAR ccc’ a / bin / echo; si passa la stringa interpolata (come espansa nell’ambiente corrente) a / bin / echo. In questo caso, poiché l’ambiente corrente non ha TESTVAR , stai semplicemente passando ‘aaa ccc’ al comando.
La documentazione potrebbe essere molto più chiara. Per fortuna c’è Stack Overflow!
http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment
Per ottenere ciò che vuoi, usa
( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )
Ragionare:
È necessario separare l’assegnazione per punto e virgola o nuova riga dal comando successivo, altrimenti non viene eseguita prima che si verifichi l’ espansione dei parametri per il comando successivo (echo).
È necessario eseguire l’assegnazione all’interno di un ambiente di subshell , per assicurarsi che non permanga oltre la linea corrente.
Questa soluzione è più breve, più ordinata e più efficiente rispetto a quanto suggerito da altri, in particolare non crea un nuovo processo.
Il motivo è che questo imposta una variabile di ambiente per una riga. Ma, l’ echo
non fa l’espansione, bash
fa. Quindi, la variabile viene effettivamente espansa prima dell’esecuzione del comando, anche se SOME_VAR
è BBB
nel contesto del comando echo.
Per vedere l’effetto, puoi fare qualcosa come:
$ SOME_VAR=BBB bash -c 'echo $SOME_VAR' BBB
Qui la variabile non viene espansa fino all’esecuzione del processo figlio, quindi viene visualizzato il valore aggiornato. se controlli di nuovo SOME_VARIABLE
nella shell parent, è ancora AAA
, come previsto.
SOMEVAR=BBB; echo zzz $SOMEVAR zzz
Usare un ; separare le affermazioni che sono sulla stessa linea.
Ecco una alternativa:
SOMEVAR=BBB && echo zzz $SOMEVAR zzz