¿Qué es el comando "eval" en bash?


177

¿Qué puedes hacer con el evalcomando? ¿Por qué es útil? ¿Es algún tipo de función incorporada en bash? No hay manpágina para eso ...


28
Utilícelo type commandpara saber de qué tipo es un comando . ( type evalen este caso)
rozcietrzewiacz

99
"eval is an shell builtin"
Nuno Rafael Figueiredo

3
help evalobtener la página "man" de su shell
m-ric

eval es un bash-builtin y está documentado en la página del manual de bash. Así que simplemente escriba "man bash" y busque la sección apropiada para eval. Esto también se aplica a otras bash-builtins.
Dirk Thannhäuser

Respuestas:


132

evalEs parte de POSIX. Es una interfaz que puede ser un shell incorporado.

Se describe en el "Manual del programador POSIX": http://www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

Tomará un argumento y construirá un comando del mismo, que será ejecutado por el shell. Este es el ejemplo de la página de manual:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. En la primera línea se define $foocon el valor '10'y $xcon el valor 'foo'.
  2. Ahora defina $y, que consiste en la cadena '$foo'. El signo de dólar se debe escapar con '$'.
  3. Para verificar el resultado echo $y,.
  4. El resultado será la cadena. '$foo'
  5. Ahora repetimos la tarea con eval. Primero evaluará $xa la cadena 'foo'. Ahora tenemos la declaración y=$fooque será evaluada y=10.
  6. El resultado de echo $yahora es el valor '10'.

Esta es una función común en muchos idiomas, por ejemplo, Perl y JavaScript. Eche un vistazo a perldoc eval para obtener más ejemplos: http://perldoc.perl.org/functions/eval.html


44
En terminología de shell, evales una función incorporada, no una función. En la práctica, las funciones incorporadas se comportan de manera muy similar a las funciones que no tienen una definición en el lenguaje, pero no del todo (como se hace evidente si está lo suficientemente retorcido como para definir una función llamada eval).
Gilles

gracias, arreglado eso :-)
echox

44
¿Cuál es la diferencia entre eval y backticks '?
JohnyTex

55
Los backticks son una forma abreviada de ejecutar otro shell completo, lo que significa que tendrás otro proceso bash / sh secundario ejecutándose dentro de tu script.
Aaron R.

¿Por qué echo $ y (etapa 3) trae foo (10) en lugar de $ foo? (es decir, solo el valor de foo en lugar de la cadena $ + el valor de foo)?
JohnDoea

60

Sí, evales un comando interno de bash, por lo que se describe en la bashpágina de manual.

eval [arg ...]
    The  args  are read and concatenated together into a single com-
    mand.  This command is then read and executed by the shell,  and
    its  exit status is returned as the value of eval.  If there are
    no args, or only null arguments, eval returns 0.

Por lo general, se usa en combinación con una sustitución de comando . Sin un explícito eval, el shell intenta ejecutar el resultado de una sustitución de comando, no evaluarlo .

Digamos que desea codificar un equivalente de VAR=value; echo $VAR. Tenga en cuenta la diferencia en cómo el shell maneja los escritos de echo VAR=value:

  1. andcoz@...:~> $( echo VAR=value )
    bash: VAR=value: command not found
    andcoz@...:~> echo $VAR
    <empty line>

    El shell intenta ejecutarse echoy VAR=valuecomo dos comandos separados. Lanza un error sobre la segunda cadena. La asignación sigue siendo ineficaz.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    El shell fusiona (concatena) las dos cadenas echoy VAR=valueanaliza esta unidad individual de acuerdo con las reglas apropiadas y la ejecuta.

Por último, pero no menos importante, evalpuede ser un comando muy peligroso. Cualquier entrada a un evalcomando debe verificarse cuidadosamente para evitar problemas de seguridad.


18

evalno tiene página de manual porque no es un comando externo separado, sino un shell incorporado, lo que significa un comando interno y conocido solo por el shell ( bash). La parte relevante de la bashpágina del manual dice:

eval [arg ...]
    The args are read and concatenated together into a single command.  
    This command is then  read  and executed by the shell, and its exit 
    status is returned as the value of eval.  If there are no args, or only 
    null arguments, eval returns 0

Además, la salida si help evales:

eval: eval [arg ...]
    Execute arguments as a shell command.

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.

evales un comando poderoso y si tiene la intención de usarlo, debe tener mucho cuidado para evitar los posibles riesgos de seguridad que conlleva.


16

La instrucción eval le dice al shell que tome los argumentos de eval como comando y los ejecute a través de la línea de comandos. Es útil en una situación como la siguiente:

En su script, si está definiendo un comando en una variable y luego desea usar ese comando, entonces debe usar eval:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >

3
El comentario en la línea 4 de su ejemplo es incorrecto: debería ser "El comando anterior no funcionó porque bash intentó encontrar un comando llamado ls | more. En otras palabras: el nombre del comando único constaba de nueve caracteres, incluidos los espacios y el símbolo de la tubería .
cfi

Tengo exactamente el mismo comportamiento que informó. bash --version == GNU bash, versión 3.2.57 (1) -release (x86_64-apple-darwin18)
Alexander Bird

10

¿Qué es eval?

eval es un comando de shell que generalmente se implementa como incorporado.

En POSIX aparece como parte de "2.14. Utilidades especiales incorporadas" en la entrada "eval" .
Lo que significa construir es:

El término "incorporado" implica que el shell puede ejecutar la utilidad directamente y no necesita buscarla.

¿Qué hace?

En términos simples: hace que una línea de entrada se analice dos veces .

¿Como hace eso?

El shell tiene una secuencia de pasos que sigue para "procesar" una línea. Podrías mirar esta imagen y darte cuenta de que eval es la única línea que sube, de vuelta al paso 1, a la izquierda. De la descripción POSIX :

2.1 Introducción de Shell

  1. El shell lee su entrada ...
  2. El shell divide la entrada en tokens: palabras y operadores.
  3. El shell analiza la entrada en comandos simples y compuestos.
  4. El shell realiza varias expansiones (por separado) ...
  5. El shell realiza la redirección y elimina los operadores de redirección y sus operandos de la lista de parámetros.
  6. El shell ejecuta una función, un archivo ejecutable incorporado o un script ...
  7. El shell opcionalmente espera a que se complete el comando y recopila el estado de salida.

En el paso 6 se ejecutará un incorporado.
En el paso 6 eval hace que la línea procesada se envíe de vuelta al paso 1.
Es la única condición bajo la cual la secuencia de ejecución retrocede.

Por eso digo: con eval, una línea de entrada se analiza dos veces .

Efectos del análisis dos veces.

El primero.

Y efecto más importante para entender. Es esa una consecuencia de la primera vez que una línea está sujeta a los siete pasos de shell que se muestran arriba, es la cita . Dentro del paso 4 (expansiones), también hay una secuencia de pasos para realizar todas las expansiones , la última de las cuales es Quitar eliminación :

La eliminación de la cotización siempre se realizará en último lugar.

Entonces, siempre, hay un nivel de cita eliminado.

Segundo.

Como consecuencia de ese primer efecto, partes adicionales / diferentes de la línea quedan expuestas al análisis del shell y a todos los demás pasos.

Ejemplo.

Indirección

Eso permite ejecutar expansiones indirectas:

a=b b=c    ;    eval echo \$$a            ### shall produce "c"

¿Por qué? Porque en el primer bucle, $se cita el primero .
Como tal, el shell lo ignora para las expansiones.
El siguiente $con el nombre a se expande para producir "b".
Luego, se elimina un nivel de $comillas , lo que hace que la primera no se cite.
Fin del primer ciclo.

Es entonces, en el segundo bucle que $bel shell lee la cadena .
Luego se expandió a "c"
y se dio como argumento para echo.

Para "ver" qué eval producirá en el primer bucle (para ser evaluado nuevamente), use echo. O cualquier comando / script / programa que muestre claramente los argumentos:

$ a=b b=c
$ eval echo \$$a;
c

Reemplace eval por echo para "ver" lo que está sucediendo:

$ echo echo \$$a
echo $b

También es posible mostrar todas las "partes" de una línea con:

$ printf '<%s> ' echo \$$a
<echo> <$b>

Que, en este ejemplo, es solo un eco y una variable, pero recuérdelo para ayudar a evaluar casos más complejos.

Una corrección

Hay que decir que: hay un error en el código anterior, ¿puedes verlo?
Fácil: faltan algunas citas.

¿Cómo? Tu puedes preguntar. Simple, cambiemos las variables (no el código):

$ a=b b="hi     jk"
$ eval echo \$$a
hi jk

¿Ves los espacios faltantes?
Eso es porque el valor en el interior $bfue dividido por el shell.

Si eso no te convence, prueba esto:

$ a=b b="hi  *  jk"
$ eval echo \$$a              ### warning this will expand to the list  
                              ### of all files in the present directory.

¿Por qué?

Faltan citas. Para que funcione correctamente (agregue comillas internas "$a"y externas \").
Prueba esto (es perfectamente seguro):

$ a=b b="hi      *       jk"
$ eval echo \" \$"$a" \"
hi      *       jk

Sobre el manual:

No hay página de manual para ello.

No, no hay una página de manual independiente para esto. La búsqueda de manual con man -f evalo incluso apropos evalno muestra ninguna entrada.

Está incluido en el interior man bash. Como es cualquier incorporado.
Busque "SHELL BUILTIN COMMANDS" y luego "eval".

Una forma más fácil de obtener ayuda es: en bash, puede hacer help evalpara ver la ayuda para el incorporado.

¿Por qué eval se llama malvado?

Porque es vinculante el texto para codificar dinámicamente.

En otras palabras: convierte la lista de sus argumentos (y / o expansiones de tales argumentos) en una línea ejecutada. Si por algún motivo, un atacante ha establecido un argumento, estará ejecutando el código del atacante.

O incluso más simple, con eval le está diciendo a quien haya definido el valor de uno o varios argumentos:

Vamos, siéntate aquí y escribe cualquier línea de comando, la ejecutaré con mis poderes.

¿Eso es peligroso? Debe quedar claro para todos que lo es.

La regla de seguridad para eval debe ser:
Ejecutar eval solo en variables a las que le haya dado su valor.

Lea más detalles aquí .


10

evalEs una característica de los idiomas más interpretadas ( TCL, python, ruby...), no sólo las conchas. Se utiliza para evaluar el código dinámicamente.

En shells, se implementa como un comando incorporado de shell.

Básicamente, evaltoma una cadena como argumento y evalúa / interpreta el código que contiene. En shells, evalpuede tomar más de un argumento, pero evalsolo los concatena para formar la cadena para evaluar.

Es muy poderoso porque puedes construir código dinámicamente y ejecutarlo, algo que no puedes hacer en lenguajes compilados como C.

Me gusta:

varname=$1 varvalue=$2
eval "$varname=\$varvalue" # evaluate a string like "foo=$varvalue"
                           # which in Bourne-like shell language
                           # is a variable assignment.

Pero también es peligroso, ya que es importante desinfectar las partes dinámicas (proporcionadas externamente) de lo que se pasa evalpor la misma razón por la que se interpreta como código shell.

Por ejemplo, arriba si $1es evil-command; var, evalterminaría evaluando el evil-command; var=$varvaluecódigo de shell y luego lo ejecutaría evil-command.

La maldad de a evalmenudo es exagerada.

OK, es peligroso, pero al menos sabemos que es peligroso.

Una gran cantidad de otros comandos evaluará código shell en sus argumentos si no se limpia, al igual que (dependiendo de la cáscara), [aka test, export, printf, GNU sed, awky, por supuesto sh/ bash/ perly todos los intérpretes ...

Ejemplos (aquí usando unamecomo evil-commandy $acomo datos no desinfectados proporcionados externamente):

$ a='$(uname>&2)' sh -c 'eval "echo $a"'
Linux

$ a='x[0$(uname>&2)]' mksh -c 'export "$a=$b"'
Linux
$ a='x[0$(uname>&2)]' ksh93 -c 'printf "%d\n" "$a"'
Linux
0
$ a='x[0$(uname>&2)]' ksh93 -c '[ "$a" -gt 0 ]'
Linux
$ a=$'bar/g;e uname>&2\n;s//'; echo foo | sed "s/foo/$a/g"
Linux
bar
$ a='";system("uname");"'; awk "BEGIN{print \"$a\"}"

Linux
$ a=';uname'; sh -c "echo $a"

Linux

Esos sed, export... los comandos podrían considerarse más peligrosos porque, aunque es obvio, eval "$var"hará que el contenido $varsea ​​evaluado como código shell, no es tan obvio con sed "s/foo/$var/"o export "$var=value"o [ "$var" -gt 0 ]. El peligro es el mismo, pero está oculto en esos otros comandos.


@BinaryZebra, sedcomo evalse le está pasando una cadena contenida en una variable, y para ambos, el contenido de esa cadena termina siendo evaluado como código de shell, por lo que sedes tan peligroso como eval, eso es lo que estoy diciendo. sedse le pasa una cadena que contiene uname(uname no se ha ejecutado hasta ahora) y mediante la invocación de sed, el comando uname termina ejecutándose. Me gusta para eval. En sed 's/foo/$a/g', no estás pasando datos no desinfectados sed, eso no es de lo que estamos hablando aquí.
Stéphane Chazelas

2

Este ejemplo puede arrojar algo de luz:

#!/bin/bash
VAR1=25
VAR2='$VAR1'
VAR3='$VAR2'
echo "$VAR3"
eval echo "$VAR3"
eval eval echo "$VAR3"

Salida del script anterior:

$VAR2
$VAR1
25

Gracias por su contribución, pero, por divertidos que sean, no estamos realmente interesados ​​en compilar una lista de ejemplos (ligeramente diferentes) de uso eval. ¿Cree que hay algún aspecto fundamental y crítico de la funcionalidad evalque no está cubierto en las respuestas existentes? Si es así, explíquelo y use el ejemplo para ilustrar su explicación. Por favor no responda en los comentarios; edite  su respuesta para que sea más clara y completa.
Scott,
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.