¿Cómo ejecutar el comando almacenado en una variable?

83

¿Cuál es la forma correcta de llamar a algún comando almacenado en una variable?
¿Hay alguna diferencia entre 1 y 2?

#!/bin/sh
cmd="ls -la $APPROOTDIR | grep exception"
#1
$cmd
#2
eval "$cmd"
Volodymyr Bezuglyy
fuente
7
Consulte BashFAQ / 050 .
Pausado hasta nuevo aviso.

Respuestas:

95

Los shells de Unix operan una serie de transformaciones en cada línea de entrada antes de ejecutarlas. Para la mayoría de los shells, se parece a esto (tomado de la página de bashmanual):

  • división inicial de palabras
  • expansión del corsé
  • expansión de tilde
  • expansión de parámetros, variables y aritmética
  • sustitución de comando
  • división de palabras secundarias
  • expansión de ruta (también conocida como globbing)
  • eliminación de cotizaciones

El uso $cmddirecto lo reemplaza por su comando durante la fase de expansión de parámetros, y luego se somete a todas las transformaciones siguientes.

El uso eval "$cmd"no hace nada hasta la fase de eliminación de la cotización, donde $cmdse devuelve como está y se pasa como un parámetro a eval, cuya función es ejecutar toda la cadena nuevamente antes de ejecutar.

Entonces, básicamente, son iguales en la mayoría de los casos y difieren cuando su comando hace uso de los pasos de transformación hasta la expansión de parámetros. Por ejemplo, usando la expansión de llaves:

$ cmd="echo foo{bar,baz}"
$ $cmd
foo{bar,baz}
$ eval "$cmd"
foobar foobaz
JB.
fuente
6
¿Cómo hacerlo eval "$cmd"sin escribir eval? $($cmd)? ${$cmd}?
Steven Lu
2
@StevenLu, ninguno de ellos es equivalente, intencionalmente: una evaloperación analiza los datos como sintaxis; por lo tanto, es muy sensible a la seguridad, y hacerlo implícitamente sería de muy mala forma.
Charles Duffy
5

Si simplemente lo hacemos eval $cmd cuando lo hacemos cmd="ls -l"(de forma interactiva y en un script) obtenemos el resultado deseado. En su caso, tiene una tubería con un grep sin un patrón, por lo que la parte grep fallará con un mensaje de error. Solo $cmdgenerará un mensaje de "comando no encontrado" (o algo así). Así que intente usar eval y use un comando terminado, no uno que genere un mensaje de error.

Henno Brandsma
fuente
Fue sólo un error de imprenta. Debería ser "excepción grep".
Volodymyr Bezuglyy
luego use eval, no $ cmd por sí mismo, ya que probablemente no funcionará (no lo hizo en mis pruebas, bajo bash y zsh). Esto es lo que estaba destinado a hacer eval ...
Henno Brandsma
0

$cmdsimplemente reemplazaría la variable con su valor para ser ejecutado en la línea de comando. eval "$cmd"hace expansión de variables y sustitución de comandos antes de ejecutar el valor resultante en la línea de comandos

El segundo método es útil cuando desea ejecutar comandos que no son flexibles, por ejemplo.
for i in {$a..$b}
El bucle de formato no funcionará porque no permite variables.
En este caso, una tubería a bash o eval es una solución.

Probado en Mac OSX 10.6.8, Bash 3.2.48

Zimba
fuente
-4

Creo que deberías poner

`

(comillas invertidas) símbolos alrededor de su variable.

Nickolodeon
fuente
4
Esto ejecuta la salida del comando, que por ejemplo, en el caso de ls -l generará un mensaje como "total" comando no encontrado "(porque total ... es parte de la salida de ls -l, por ejemplo). NO es lo que quieres.
Henno Brandsma