eval
accetta una stringa come argomento e la valuta come se avessi digitato quella stringa su una riga di comando. (Se passi diversi argomenti, vengono prima uniti con spazi tra loro.)
${$n}
è un errore di sintassi in bash. All'interno delle parentesi graffe, puoi avere solo un nome di variabile, con alcuni possibili prefissi e suffissi, ma non puoi avere una sintassi bash arbitraria e in particolare non puoi usare l'espansione delle variabili. C'è un modo per dire "il valore della variabile il cui nome è in questa variabile", però:
echo ${!n}
one
$(…)
esegue il comando specificato all'interno delle parentesi in una subshell (ovvero in un processo separato che eredita tutte le impostazioni come i valori delle variabili dalla shell corrente) e ne raccoglie l'output. Quindi echo $($n)
esegue $n
come comando di shell e ne visualizza l'output. Dal $n
restituisce 1
, $($n)
tenta di eseguire il comando 1
, che non esiste.
eval echo \${$n}
esegue i parametri passati a eval
. Dopo l'espansione, i parametri sono echo
e ${1}
. Quindi eval echo \${$n}
esegue il comando echo ${1}
.
Nota che la maggior parte delle volte devi usare le virgolette doppie per sostituire le variabili e sostituire i comandi (ad esempio ogni volta che c'è un $
):"$foo", "$(foo)"
. Metti sempre le doppie virgolette attorno alle sostituzioni di variabili e comandi , a meno che tu non sappia che devi lasciarli fuori. Senza le virgolette doppie, la shell esegue la divisione del campo (ovvero divide il valore della variabile o l'output del comando in parole separate) e quindi tratta ogni parola come un modello di carattere jolly. Ad esempio:
$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *
eval
non è usato molto spesso. In alcune shell, l'uso più comune è ottenere il valore di una variabile il cui nome non è noto fino al runtime. In bash, questo non è necessario grazie al ${!VAR}
sintassi. eval
è comunque utile quando devi costruire un comando più lungo contenente operatori, parole riservate, ecc.
Pensa semplicemente a eval come "valutare la tua espressione un'altra volta prima dell'esecuzione"
eval echo \${$n}
diventa echo $1
dopo il primo giro di valutazione. Tre modifiche da notare:
- Il
\$
è diventato$
(La barra rovesciata è necessaria, altrimenti tenta di valutare${$n}
, che significa una variabile denominata{$n}
, che non è consentito) $n
è stato valutato a1
- Il
eval
scomparso
Nel secondo round, è fondamentalmente echo $1
che può essere eseguito direttamente.
Quindi eval <some command>
valuterà prima <some command>
(per valutare qui intendo sostituire le variabili, sostituire i caratteri sfuggiti con quelli corretti ecc.), quindi eseguire nuovamente l'espressione risultante.
eval
viene utilizzato quando si desidera creare dinamicamente variabili o leggere output da programmi specificamente progettati per essere letti in questo modo. Vedere http://mywiki.wooledge.org/BashFAQ/048 per esempi. Il collegamento contiene anche alcuni modi tipici in cui eval
viene utilizzato e i rischi ad esso associati.
Nella mia esperienza, un uso "tipico" di eval è per l'esecuzione di comandi che generano comandi shell per impostare variabili d'ambiente.
Forse hai un sistema che utilizza una raccolta di variabili d'ambiente e hai uno script o un programma che determina quali dovrebbero essere impostati e i loro valori. Ogni volta che esegui uno script o un programma, viene eseguito in un processo biforcuto, quindi tutto ciò che fa direttamente alle variabili di ambiente viene perso quando esce. Ma quello script o programma può inviare i comandi di esportazione a stdout.
Senza eval, dovresti reindirizzare stdout a un file temporaneo, generare il file temporaneo e quindi eliminarlo. Con eval, puoi semplicemente:
eval "$(script-or-program)"
Nota che le virgolette sono importanti. Prendi questo esempio (artificioso):
# activate.sh
echo 'I got activated!'
# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")
$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier
$ eval "$(python test.py)"
I got activated!