Bash: specificare le variabili d’ambiente per echo sulla riga di comando?

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:

  1. SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz .
  2. 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

Il problema, rivisitato

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.

Passi dell’interprete

Un’altra parte del problema è che Bash definisce questi passaggi per il suo interprete:

  1. Legge il suo input da un file (vedi Shell Scripts), da una stringa fornita come argomento all’opzione -c invocation (vedere Invocazione di Bash), o dal terminale dell’utente.
  2. Rompe l’input in parole e operatori, obbedendo alle regole di quoting descritte in Quoting. Questi token sono separati da metacaratteri. L’espansione dell’alias viene eseguita da questo passaggio (vedi Alias).
  3. Analizza i token in comandi semplici e composti (vedi Comandi della shell).
  4. Esegue le varie espansioni di shell (vedere Espansioni di shell), suddividendo i token espansi in elenchi di nomi di file (vedere Espansione del nome file) e comandi e argomenti.
  5. Esegue tutti i reindirizzamenti necessari (vedere Reindirizzamenti) e rimuove gli operatori di reindirizzamento e i relativi operandi dall’elenco degli argomenti.
  6. Esegue il comando (consultare Esecuzione comandi).
  7. Opzionalmente attende che il comando completi e raccolga il suo stato di uscita (vedere Stato di uscita).

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.

Sommario

La documentazione potrebbe essere molto più chiara. Per fortuna c’è Stack Overflow!

Guarda anche

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