Aggiornamento: alcuni dicono che si dovrebbe -non usare mai eval. Non sono d'accordo. Penso che il rischio insorga quando si può passare a un input corrotto eval
. Tuttavia ci sono molte situazioni comuni in cui ciò non rappresenta un rischio, e quindi vale la pena sapere come utilizzare eval in ogni caso. Questa risposta allo stackover spiega i rischi di eval e le alternative a eval. In definitiva spetta all'utente determinare se / quando eval è sicuro ed efficiente da usare.
L' eval
istruzione bash consente di eseguire righe di codice calcolate o acquisite dallo script bash.
Forse l'esempio più semplice sarebbe un programma bash che apre un altro script bash come file di testo, legge ogni riga di testo e lo usa eval
per eseguirli in ordine. Questo è essenzialmente lo stesso comportamento dell'istruzione bash source
, che è ciò che si vorrebbe usare, a meno che non fosse necessario eseguire un qualche tipo di trasformazione (ad esempio filtro o sostituzione) sul contenuto dello script importato.
Raramente ne ho avuto bisogno eval
, ma ho trovato utile leggere o scrivere variabili i cui nomi erano contenuti in stringhe assegnate ad altre variabili. Ad esempio, per eseguire azioni su insiemi di variabili, mantenendo l'ingombro del codice ridotto ed evitando la ridondanza.
eval
è concettualmente semplice. Tuttavia, la sintassi rigorosa del linguaggio bash e l'ordine di analisi dell'interprete bash possono essere sfumati e rendere eval
criptici e difficili da usare o da capire. Ecco gli elementi essenziali:
L'argomento passato a eval
è un'espressione stringa calcolata in fase di esecuzione. eval
eseguirà il risultato finale analizzato del suo argomento come una riga di codice effettiva nel tuo script.
La sintassi e l'ordine di analisi sono rigorosi. Se il risultato non è una riga eseguibile di codice bash, nell'ambito del tuo script, il programma si arresterà in modo anomalo eval
sull'istruzione mentre tenta di eseguire la spazzatura.
Durante il test è possibile sostituire l' eval
istruzione con echo
e vedere cosa viene visualizzato. Se si tratta di un codice legittimo nel contesto corrente, eseguirlo eval
funzionerà.
I seguenti esempi possono aiutare a chiarire come funziona eval ...
Esempio 1:
eval
l'istruzione davanti al codice "normale" è un NOP
$ eval a=b
$ eval echo $a
b
Nell'esempio sopra, le prime eval
affermazioni non hanno scopo e possono essere eliminate. eval
è inutile nella prima riga perché non c'è alcun aspetto dinamico nel codice, cioè è già analizzato nelle righe finali del codice bash, quindi sarebbe identico a una normale istruzione di codice nello script bash. Anche il 2 ° eval
è inutile, perché, sebbene ci sia una fase di analisi che converte $a
nella sua stringa letterale equivalente, non vi è alcuna indiretta (ad esempio nessun riferimento tramite il valore di stringa di un vero nome bash o variabile di script con bash), quindi si comporterebbe in modo identico come una riga di codice senza eval
prefisso.
Esempio 2:
Eseguire l'assegnazione var utilizzando i nomi var passati come valori stringa.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Se dovessi echo $key=$val
, l'output sarebbe:
mykey=myval
Questo , essendo il risultato finale dell'analisi delle stringhe, è ciò che verrà eseguito da eval, quindi il risultato dell'istruzione echo alla fine ...
Esempio 3:
Aggiunta di ulteriori riferimenti indiretti all'esempio 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Quanto sopra è un po 'più complicato dell'esempio precedente, basandosi maggiormente sull'ordine di analisi e sulle peculiarità di bash. La eval
riga verrebbe approssimativamente analizzata internamente nel seguente ordine (nota che le seguenti affermazioni sono pseudocodice, non codice reale, solo per tentare di mostrare come l'istruzione verrebbe suddivisa in passaggi internamente per arrivare al risultato finale) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Se l'ordine di analisi ipotizzato non spiega cosa sta facendo abbastanza eval, il terzo esempio può descrivere l'analisi in modo più dettagliato per aiutare a chiarire cosa sta succedendo.
Esempio 4:
Scopri se i var, i cui nomi sono contenuti nelle stringhe, contengono essi stessi valori di stringa.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
Nella prima iterazione:
varname="myvarname_a"
Bash analizza l'argomento eval
e lo eval
vede letteralmente in fase di esecuzione:
eval varval=\$$myvarname_a
Il seguente pseudocodice tenta di illustrare come bash interpreta la riga sopra del codice reale , per arrivare al valore finale eseguito da eval
. (le seguenti righe descrittive, non esatto codice bash):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Una volta eseguito tutto l'analisi, il risultato è ciò che viene eseguito e il suo effetto è evidente, dimostrando che non c'è nulla di particolarmente misterioso in se eval
stesso e la complessità sta nell'analisi del suo argomento.
varval="User-provided"
Il codice rimanente nell'esempio sopra verifica semplicemente se il valore assegnato a $ varval è null e, in tal caso, richiede all'utente di fornire un valore.
$($n)
viene eseguito$n
in una subshell. Tenta di eseguire il comando1
che non esiste.