¿Qué puedes hacer con el eval
comando? ¿Por qué es útil? ¿Es algún tipo de función incorporada en bash? No hay man
página para eso ...
help eval
obtener la página "man" de su shell
¿Qué puedes hacer con el eval
comando? ¿Por qué es útil? ¿Es algún tipo de función incorporada en bash? No hay man
página para eso ...
help eval
obtener la página "man" de su shell
Respuestas:
eval
Es 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
$foo
con el valor '10'
y $x
con el valor 'foo'
.$y
, que consiste en la cadena '$foo'
. El signo de dólar se debe escapar con '$'
.echo $y
,.'$foo'
eval
. Primero evaluará $x
a la cadena 'foo'
. Ahora tenemos la declaración y=$foo
que será evaluada y=10
.echo $y
ahora 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
eval
es 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
).
Sí, eval
es un comando interno de bash, por lo que se describe en la bash
pá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
:
andcoz@...:~> $( echo VAR=value )
bash: VAR=value: command not found
andcoz@...:~> echo $VAR
<empty line>
El shell intenta ejecutarse echo
y VAR=value
como dos comandos separados. Lanza un error sobre la segunda cadena. La asignación sigue siendo ineficaz.
andcoz@...:~> eval $( echo VAR=value )
andcoz@...:~> echo $VAR
value
El shell fusiona (concatena) las dos cadenas echo
y VAR=value
analiza esta unidad individual de acuerdo con las reglas apropiadas y la ejecuta.Por último, pero no menos importante, eval
puede ser un comando muy peligroso. Cualquier entrada a un eval
comando debe verificarse cuidadosamente para evitar problemas de seguridad.
eval
no 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 bash
pá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 eval
es:
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.
eval
es un comando poderoso y si tiene la intención de usarlo, debe tener mucho cuidado para evitar los posibles riesgos de seguridad que conlleva.
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 >
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 .
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.
En términos simples: hace que una línea de entrada se analice dos veces .
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
- El shell lee su entrada ...
- El shell divide la entrada en tokens: palabras y operadores.
- El shell analiza la entrada en comandos simples y compuestos.
- El shell realiza varias expansiones (por separado) ...
- El shell realiza la redirección y elimina los operadores de redirección y sus operandos de la lista de parámetros.
- El shell ejecuta una función, un archivo ejecutable incorporado o un script ...
- 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 .
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.
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.
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 $b
el 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.
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 $b
fue 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.
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
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 eval
o incluso apropos eval
no 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 eval
para ver la ayuda para el incorporado.
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í .
eval
Es 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, eval
toma una cadena como argumento y evalúa / interpreta el código que contiene. En shells, eval
puede tomar más de un argumento, pero eval
solo 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 eval
por la misma razón por la que se interpreta como código shell.
Por ejemplo, arriba si $1
es evil-command; var
, eval
terminaría evaluando el evil-command; var=$varvalue
código de shell y luego lo ejecutaría evil-command
.
La maldad de a eval
menudo 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
, awk
y, por supuesto sh
/ bash
/ perl
y todos los intérpretes ...
Ejemplos (aquí usando uname
como evil-command
y $a
como 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 $var
sea 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.
sed
como eval
se 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 sed
es tan peligroso como eval
, eso es lo que estoy diciendo. sed
se 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í.
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
eval
. ¿Cree que hay algún aspecto fundamental y crítico de la funcionalidad eval
que 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.
type command
para saber de qué tipo es un comando . (type eval
en este caso)