¿Cuál es la diferencia entre "eval" y "source / dev / stdin"?

17

Entre las siguientes alternativas ...

  1. con eval.

    comd="ls"
    eval "$comd"
  2. con source /dev/stdin

    printf "ls" | source /dev/stdin
  3. con source /dev/stdiny ( )o{ }

    ( printf "ls" ) | source /dev/stdin
    { printf "ls"; } | source /dev/stdin

    (Cuando nos encontramos printfen { }, ¿hay alguna otra ventaja que no usar subnivel?)

    • ¿Cuál es la diferencia entre ellos?

    • ¿Cuál es el preferido?

    • ¿Cuál es la forma preferida de ejecutar comandos? ()o {}?

MS.Kim
fuente
1
No recomendaría ninguno de los dos enfoques. ¿Qué estás tratando de hacer , que crees que necesitas ejecutar código arbitrario enviado por un usuario?
chepner
2
También pensé que estaban ejecutando una entrada arbitraria del usuario (como es), hasta que leí la pregunta. Pero puede ser que esté prediciendo que lo harán.
ctrl-alt-delor

Respuestas:

17
  • ¿Cuál es la diferencia entre las formas?

de bash manpage:

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.

source filename [arguments]
              Read and execute commands from filename  in  the  current  shell
              environment  and return the exit status of the last command exe
              cuted from filename.  If filename does not contain a slash, file
              names  in  PATH  are used to find the directory containing file
              name.  The file searched for in PATH  need  not  be  executable.
              When  bash  is  not  in  posix  mode,  the  current directory is
              searched if no file is found in PATH.  If the sourcepath  option
              to  the  shopt  builtin  command  is turned off, the PATH is not
              searched.  If any arguments are supplied, they become the  posi
              tional  parameters  when  filename  is  executed.  Otherwise the
              positional parameters are unchanged.  The return status  is  the
              status  of  the  last  command exited within the script (0 if no
              commands are executed), and false if filename is  not  found  or
              cannot be read.

No hay diferencias entre las dos formas.

Solo hay una nota: evalconcatena todos sus argumentos, que luego se ejecutan como un solo comando. sourcelee el contenido de un archivo y los ejecuta. evalsolo puede construir comandos a partir de sus argumentos, no stdin. Entonces no puedes hacer así:

printf "ls" | eval
  • ¿Cuál es más preferido?

Su ejemplo proporciona el mismo resultado, pero el propósito de evaly sourcees diferente. sourcegeneralmente se usa para proporcionar una biblioteca para otros scripts, mientras evalque solo se usa para evaluar comandos. Debe evitar el uso evalsi es posible, porque no hay garantía de que la cuerda evadida esté limpia; debemos hacer algunos controles de cordura, utilizando en su subshelllugar.

  • Si ejecutamos algunos comandos en () o {}, ¿cuál es más preferido?

Cuando ejecuta comandos de secuencias dentro de llaves { }, todos los comandos se ejecutan en el shell actual , en lugar de un subshell (que es el caso si se ejecuta entre paréntesis (ver referencia de bash )).

El uso subshell ( )utiliza más recursos, pero su entorno actual no se ve afectado. El uso { }ejecuta todos los comandos en el shell actual, por lo que su entorno se ve afectado. Dependiendo de su propósito, puede elegir uno de ellos.

Cuonglm
fuente
2
Creo que entendiste mal la pregunta. Por supuesto, no puede reemplazar evalpor source. Supongo que la pregunta es: es eval "$cmd"equivalente a echo "$cmd" | source /dev/stdin. Mi opinión actual es: sí.
Hauke ​​Laging
3

La principal diferencia es que las formas 2 y 3 están usando una tubería, lo que obligará a bash a ejecutar el comando "fuente" en una subshell (a menos que se configure lastpipe, solo disponible en bash 4.2+), lo que lo hará bastante equivalente a :

printf "ls" | bash

Las consecuencias son que cualquier variable de entorno establecida por su código se perderá, por lo que esto no funcionará como se esperaba:

printf "abc=2" | source /dev/stdin

Para ejecutar los comandos en el shell actual, puede usar la sustitución de procesos:

source <(printf "abc=2")

Puede poner más comandos dentro de paréntesis usando punto y coma como de costumbre.

Si elimina la tubería de esta manera, creo que no hay diferencia entre usar "eval" y "source". Debe preferir el que sea más simple de usar en su caso particular:

  • si ya tiene comandos para ejecutar en variable, use "eval"
  • si los tiene en un archivo o los obtiene de un comando externo, use "fuente"
Kamil Christ
fuente
0

Como complemento a las respuestas ya dadas:

Un sourceequivalente a ...

comd="ls"
eval "$comd"

... es ...

source <(printf ls)

En caso de que lsno haya una diferencia significativa.

Pero en el caso de un comando que tenga la intención de afectar su entorno actual (lo que generalmente piensa cuando usa source) esta variante lo haría (como su primera solución con evaltambién lo haría), mientras que su segundo enfoque solo afecta el entorno de una subshell que ganó ' No estará disponible después de ejecutar su línea de código.

yaccob
fuente