“ eval”和“ source / dev / stdin”有什么区别?


17

在以下替代方案之间...

  1. eval

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

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

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

    (当我们在printf中运行时{ },除了不使用subshel​​l之外,还有什么好处吗?)

    • 它们之间有什么区别?

    • 哪个是首选?

    • 运行命令的首选方法是哪种?()还是{}


1
我不推荐任何一种方法。您实际上想做什么,以为您需要执行用户提交的任意代码?
chepner 2014年

2
我也尽管他们正在执行任意用户输入(按原样进行),直到我阅读了问题。但是也许您正在预测他们会的。
ctrl-alt-delor

Answers:


17
  • 两种方式有什么区别?

来自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.

两种方式之间没有区别。

只有一个注释:eval将其所有参数串联在一起,然后将其作为单个命令运行。source读取文件的内容并执行它们。eval只能根据其参数构建命令,而不能根据其参数构建命令stdin。所以你不能这样:

printf "ls" | eval
  • 哪个更优选?

你举的例子提供了相同的结果,但目的evalsource是不同的。source通常用于为其他脚本提供库,而eval仅用于评估命令。eval如果可能的话,应避免使用,因为不能保证逃逸的字符串是干净的。我们必须做一些检查,subshell改为使用。

  • 如果我们在()或{}中运行一些命令,哪个更优选?

在花括号内运行sequence命令时{ },所有命令均在当前shell中运行,而不是在子shell中运行(如果在括号内运行,则是这种情况(请参见bash 参考))。

使用会subshell ( )占用更多资源,但是您的当前环境不会受到影响。使用using { }在当前shell中运行所有命令,因此您的环境受到影响。根据您的目的,您可以选择其中之一。


2
我认为您误解了这个问题。当然,你不能代替evalsource。我想问题是:eval "$cmd"等于echo "$cmd" | source /dev/stdin。我目前的意见是:是的。
Hauke Laging

3

主要区别在于第二和第三形式使用管道,这将迫使bash在子shell中运行“ source”命令(除非设置了lastpipe,仅在bash 4.2+中可用),这将使其等效于:

printf "ls" | bash

结果是您的代码设置的所有环境变量都将丢失,因此这将无法正常工作:

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

要在当前shell中运行命令,可以使用进程替换:

source <(printf "abc=2")

您可以照常使用分号将更多命令放在括号内。

如果您以这种方式消除管道,我相信使用“ eval”和“ source”之间没有区别。在特定情况下,您应该选择一种更易于使用的方式:

  • 如果您已经有要在变量中运行的命令,请使用“ eval”
  • 如果您将它们存储在文件中或从外部命令获取它们,请使用“源”

0

作为对已经给出的答案的补充:

一个source相当于...

comd="ls"
eval "$comd"

...是...

source <(printf ls)

ls没有明显差异的情况下。

但是,如果要使用的命令会影响您当前的环境(使用时通常的意图source),则此变体会做到这一点(与您的第一种解决方案eval一样),而第二种方法只会影响子外壳的环境,而该子外壳不会在执行您的代码行之后不可用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.