Actualización: Algunas personas dicen que uno nunca debe usar eval. Estoy en desacuerdo. Creo que el riesgo surge cuando se puede pasar información corrupta eval
. Sin embargo, hay muchas situaciones comunes en las que eso no es un riesgo y, por lo tanto, vale la pena saber cómo usar eval en cualquier caso. Esta respuesta de stackoverflow explica los riesgos de eval y las alternativas a eval. En última instancia, depende del usuario determinar si / cuando eval es seguro y eficiente de usar.
La eval
instrucción bash le permite ejecutar líneas de código calculadas o adquiridas por su script bash.
Quizás el ejemplo más sencillo sería un programa bash que abre otro script bash como un archivo de texto, lee cada línea de texto y lo utiliza eval
para ejecutarlos en orden. Ese es esencialmente el mismo comportamiento que la source
declaración bash , que es lo que se usaría, a menos que fuera necesario realizar algún tipo de transformación (por ejemplo, filtrado o sustitución) en el contenido del script importado.
Raramente lo he necesitado eval
, pero me ha resultado útil leer o escribir variables cuyos nombres estaban contenidos en cadenas asignadas a otras variables. Por ejemplo, para realizar acciones en conjuntos de variables, manteniendo la huella de código pequeña y evitando la redundancia.
eval
Es conceptualmente simple. Sin embargo, la sintaxis estricta del lenguaje bash y el orden de análisis del intérprete bash pueden matizarse y hacer que eval
parezca críptico y difícil de usar o comprender. Aquí están los elementos esenciales:
El argumento pasado a eval
es una expresión de cadena que se calcula en tiempo de ejecución. eval
ejecutará el resultado analizado final de su argumento como una línea de código real en su script.
La sintaxis y el orden de análisis son estrictos. Si el resultado no es una línea ejecutable de código bash, en el alcance de su secuencia de comandos, el programa se bloqueará en la eval
declaración al intentar ejecutar basura.
Al probar, puede reemplazar la eval
declaración con echo
y ver lo que se muestra. Si es un código legítimo en el contexto actual, ejecutarlo eval
funcionará.
Los siguientes ejemplos pueden ayudar a aclarar cómo funciona eval ...
Ejemplo 1:
eval
declaración delante del código 'normal' es un NOP
$ eval a=b
$ eval echo $a
b
En el ejemplo anterior, las primeras eval
declaraciones no tienen ningún propósito y pueden eliminarse. eval
no tiene sentido en la primera línea porque no hay un aspecto dinámico en el código, es decir, ya se analizó en las líneas finales del código bash, por lo que sería idéntico a una declaración de código normal en el script bash. El segundo también no eval
tiene sentido, porque, aunque hay un paso de análisis que se convierte $a
en su equivalente de cadena literal, no hay indirección (por ejemplo, no se hace referencia a través del valor de cadena de un sustantivo bash real o una variable de script sostenida por bash), por lo que se comportaría de manera idéntica como una línea de código sin el eval
prefijo
Ejemplo 2
Realice la asignación var usando los nombres var pasados como valores de cadena.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Si fuera a hacerlo echo $key=$val
, la salida sería:
mykey=myval
Que , siendo el resultado final del análisis de cadenas, es lo que ejecutará eval, de ahí el resultado de la declaración de eco al final ...
Ejemplo 3:
Agregar más indirección al Ejemplo 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Lo anterior es un poco más complicado que el ejemplo anterior, ya que depende más del orden de análisis y de las peculiaridades de bash. La eval
línea se analizaría internamente aproximadamente en el siguiente orden (tenga en cuenta que las siguientes declaraciones son pseudocódigo, no código real, solo para intentar mostrar cómo la declaración se desglosará internamente en pasos para llegar al resultado final) .
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
Si el orden de análisis supuesto no explica qué eval está haciendo lo suficiente, el tercer ejemplo puede describir el análisis con más detalle para ayudar a aclarar lo que está sucediendo.
Ejemplo 4
Descubra si los vars, cuyos nombres están contenidos en cadenas, contienen valores de cadena.
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
En la primera iteración:
varname="myvarname_a"
Bash analiza el argumento eval
y lo eval
ve literalmente en tiempo de ejecución:
eval varval=\$$myvarname_a
El siguiente pseudocódigo intenta ilustrar cómo bash interpreta la línea de código real anterior , para llegar al valor final ejecutado por eval
. (las siguientes líneas son descriptivas, no el código bash exacto):
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 vez que se realiza todo el análisis, el resultado es lo que se ejecuta, y su efecto es obvio, lo que demuestra que no hay nada particularmente misterioso sobre eval
sí mismo, y la complejidad está en el análisis de su argumento.
varval="User-provided"
El código restante en el ejemplo anterior simplemente prueba para ver si el valor asignado a $ varval es nulo y, de ser así, solicita al usuario que proporcione un valor.
$($n)
se ejecuta$n
en una subshell. Intenta ejecutar el comando1
que no existe.