valore temporaneo dello script bash su comando


11

Come sotto il comando,

if true; then
   IFS=":" read a b c d e f <<< "$test"

Il libro dice quando IFS ":"viene utilizzato il comando di assegnazione del valore ( ) prima del comando principale ( read a b c d e f <<< "$value"), il suo valore è temporaneamente attivo sul comando principale. Quindi, il readcomando usa delimitatore :.

Ma, come questo comando,

if true; then
   HOME="hello" echo "$HOME"

Il messaggio Echo non è ciao. Qual è il vero significato del comando precedente?

Risposte:


5

Questo si riduce a una domanda su come funziona la valutazione. Entrambi gli esempi funzionano allo stesso modo, il problema si verifica a causa di come la shell (bash, qui) espande le variabili.

Quando scrivi questo comando:

HOME="foo" echo $HOME

La $HOMEsi espande prima che il comando viene eseguito . Pertanto, viene espanso al valore originale e non a quello nuovo impostato per il comando. La HOMEvariabile è stata effettivamente modificata nell'ambiente in cui echoè in esecuzione il comando, tuttavia, si sta stampando $HOMEdal genitore.

Per illustrare, considerare questo:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

Come puoi vedere sopra, il primo comando stampa il valore temporaneamente modificato di HOMEe il secondo stampa l'originale, dimostrando che la variabile è stata modificata solo temporaneamente. Poiché il bash -c ...comando è racchiuso tra virgolette singole ( ' ') anziché doppie ( " "), la variabile non viene espansa e viene passata così com'è al nuovo processo bash. Questo nuovo processo lo espande e stampa il nuovo valore su cui è stato impostato. Puoi vedere che succede se usi set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

Come puoi vedere sopra, la variabile $HOME non viene mai passata a echo. Vede solo il suo valore espanso. Confrontare con:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Qui, a causa delle virgolette singole, la variabile e non il suo valore vengono passati al nuovo processo.


7

Quando la shell sta analizzando una linea, tokenizzerà la linea in parole, eseguirà varie espansioni (in ordine) sulle parole, quindi eseguirà il comando.

supporre test=1:2:3:4:5:6

Diamo un'occhiata a questo comando: IFS=":" read a b c d e f <<< "$test"

Dopo la tokenizzazione, si verifica l' espansione dei parametri :IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

La shell imposterà la variabile IFS per la durata del comando read e readsaprà applicare $ IFS al suo input e dare valori ai nomi delle variabili.

Questo comando ha una storia simile, ma risultati diversi: HOME="hello" echo "$HOME"

Poiché l'espansione dei parametri avviene prima dell'inizio del comando, la shell ha:

HOME="hello" echo "/home/username"

E poi, durante l'esecuzione del comando echo, il nuovo valore di $ HOME non viene utilizzato affatto.

Per ottenere ciò che stai cercando di fare, scegline uno

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

o

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

ma non scegliere il primo.


Probabilmente è meglio scegliere il primo. Almeno è significativamente più veloce. Quando eval è la risposta, a volte probabilmente stai ponendo la domanda sbagliata. Ma se qualcuno deve farlo per alcuni motivi, cambiare la risposta non rende la domanda stessa meno sbagliata. Un'altra soluzione è quella di avvolgerlo in una funzione e utilizzare local.
user23013,

Perché la evalsoluzione dovrebbe essere evitata?
DarkHeart,

Se non controlli rigorosamente l'input, devi fare molta attenzione al codice che permetti ad altre persone di iniettare nel tuo programma.
Glenn Jackman,

-1

Esistono due ambiti: variabili di ambiente e variabili locali. Le variabili di ambiente sono valide per ogni processo (vedi setenv, getenv), mentre le variabili locali sono attive solo all'interno di questa sessione di shell. (Non è una distinzione ovvia ...)

Implicito env(come nel tuo esempio) modifica l'ambiente, mentre echo ...usa quelli locali, quindi envnon ha alcun effetto.

Per modificare le variabili locali usare, diciamo,

( HOME="foo" ; echo "$HOME" )

Qui le parentesi definiscono l'ambito di questo incarico.


1
Questo non ha nulla a che fare con l'ambito delle variabili, il problema è che la variabile viene espansa prima di essere passata alla shell figlio.
terdon
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.