bash中的“ eval”命令是什么?


176

您可以使用该eval命令做什么?为什么有用?它是bash中的某种内置函数吗?没有man页面。


28
type command学习的是什么类型的命令是。(type eval在这种情况下)
rozcietrzewiacz 2011年

9
“评估是内置的外壳”
Nuno Rafael Figueiredo

3
help eval从您的外壳中获取“ man”页面
m-ric

eval是bash内置的,并记录在bash的手册页中。因此,只需键入“ man bash”并搜索适当的部分以评估。这也适用于其他bash-builtins。
DirkThannhäuser17年

Answers:


132

eval是POSIX的一部分。它是一个接口,可以是内置的shell。

它在“ POSIX程序员手册”中描述:http : //www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

它需要一个参数并构造一个命令,该命令将由外壳程序执行。这是手册页的示例:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. 在第一行中$foo,使用值'10'和定义$x'foo'
  2. 现在定义$y,它由字符串组成'$foo'。美元符号必须以来转义'$'
  3. 要检查结果,echo $y
  4. 结果将是字符串 '$foo'
  5. 现在,我们用重复分配eval。它将首先计算$x为字符串'foo'。现在我们有了y=$foo将被评估为的语句y=10
  6. echo $y现在的结果是value '10'

这是许多语言(例如Perl和JavaScript)的常用功能。有关更多示例,请查看perldoc eval:http : //perldoc.perl.org/functions/eval.html


4
用shell术语来说,eval是内置的,不是函数。实际上,内建函数的行为很像没有语言定义的函数,但没有定义(如果扭曲得足以定义一个名为的函数,这很明显eval)。
吉尔斯

thx,固定为:-)
echox

4
eval和backticks`有什么区别?
JohnyTex

5
反引号是执行另一个整个shell的简写,这意味着您将在脚本中运行另一个子bash / sh进程。
亚伦·R.

为什么echo $ y(stage 3)带回foo(10)而不是$ foo?(即只是foo的值,而不是字符串$ + foo的值)?
JohnDoea

60

是的,eval是bash内部命令,因此在bash手册页中有介绍。

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.

通常,它与Command Substitution结合使用。没有显式eval的外壳程序将尝试执行命令替换的结果,而不是对其求值

假设您要编写的等效代码VAR=value; echo $VAR。注意外壳处理以下内容的方式不同echo VAR=value

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

    Shell尝试执行echoVAR=value作为两个单独的命令。它引发有关第二个字符串的错误。分配仍然无效。

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    Shell合并(连接)两个字符串,echo然后VAR=value根据适当的规则解析此单个单元并执行它。

最后但并非最不重要的一点是,这eval可能是非常危险的命令。eval必须仔细检查命令的任何输入,以避免安全问题。


18

eval没有手册页,因为它不是独立的外部命令,而是内置的shell,这意味着shell(bash)内部的命令并且仅由shell()知道。bash手册页的相关部分显示:

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

另外,输出if help eval为:

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是一个功能强大的命令,如果要使用它,则应格外小心,以防止它附带的可能的安全风险


16

eval语句告诉外壳程序将eval的参数作为命令并通过命令行运行它们。在以下情况下很有用:

在脚本中,如果要在变量中定义命令,以后再使用该命令,则应使用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
您的示例的第4行中的注释是错误的:应该为“上述命令无效,因为bash试图找到一个名为的命令ls | more。换句话说:单个命令名称由9个字符组成,包括空格和管道符号。
CFI

我得到了他报告的完全相同的行为。bash --version == GNU bash,版本3.2.57(1)-发行版(x86_64-apple-darwin18)
Alexander Bird

10

什么是评估?

eval是一个shell命令,通常以内置方式实现。

在POSIX中,它作为条目“ eval”的“ 2.14。Special Built-In Utilities”的一部分列出。 内置的意思是:

术语“内置”表示外壳程序可以直接执行该实用程序,而无需搜索它。

它有什么作用?

简单来说:使输入行被解析两次

它是如何做到的?

外壳程序具有一系列步骤,以“处理”一行。您可以看一下这张图,并意识到eval是唯一返回到左侧第1步的行。从POSIX描述

2.1 Shell简介

  1. 外壳读取其输入..
  2. 外壳将输入分为令牌:单词和运算符
  3. Shell将输入解析为简单的复合命令。
  4. 外壳执行各种扩展(单独)...
  5. Shell执行重定向,并从参数列表中删除重定向运算符及其操作数。
  6. Shell执行函数,内置,可执行文件或脚本...
  7. Shell可以选择等待命令完成并收集退出状态。

在步骤6,将执行内置命令。
在步骤6中,eval导致将处理过的行发送回步骤1。
这是执行序列返回的唯一条件。

这就是为什么我说:用eval将输入行解析两次

解析两次的效果。

首先。

并了解最重要的效果。引用,是一行第一次受到上述七个shell步骤的结果?在第4步(扩展)中,还有 一系列执行所有扩展的步骤,其中最后一个是Quote Removal

报价删除应始终在最后执行。

因此,总是删除一级报价。

第二。

作为第一个效果的结果,该行的其他/不同部分暴露给Shell解析以及所有其他步骤。

例。

间接

这样就可以执行间接扩展:

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

为什么?因为在第一个循环上,第一个$被引用。
这样,shell扩展将忽略它。名称为a
的下$一个扩展为产生“ b”。
然后,删除一个级别的引用,使第一个不被$引用。
第一循环结束。

然后,在第二个循环中,$bshell读取了字符串。
然后扩展为“ c”,
并作为给出echo

要“查看”在第一个循环上将产生什么评估(将再次评估),请使用echo。或任何清楚显示参数的命令/脚本/程序:

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

用echo替换eval以“查看”正在发生的情况:

$ echo echo \$$a
echo $b

也可以显示一行的所有“部分”:

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

在此示例中,它只是一个回声和一个变量,但请记住它以帮助评估更复杂的情况。

更正。

必须说:上面的代码有错误,您能看到吗?
容易:缺少一些报价。

怎么样?你可能会问。简单,让我们更改变量(而不是代码):

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

看到缺少的空格?
那是因为里面的值$b被外壳分割了。

如果那不能说服您,请尝试以下操作:

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

为什么?

缺少引号。为了使其正常工作(添加内部"$a"和外部\"引号)。
试试这个(绝对安全):

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

关于手册:

没有手册页。

不,没有独立的手册页。搜索手册,man -f eval甚至不apropos eval显示任何条目。

它包含在内部man bash。就像任何内置的一样。
搜索“ SHELL BUILTIN COMMANDS”,然后搜索“ eval”。

获得帮助的更简单方法是:在bash中,您可以help eval查看内置帮助。

为什么称eval为邪恶?

因为它将文本绑定为动态代码。

换句话说:它将参数列表(和/或此类参数的扩展)转换为已执行的行。如果由于某种原因攻击者设置了参数,则您将执行攻击者代码。

甚至更简单,使用eval可以告诉谁定义了一个或几个参数的值:

来吧,坐在这里,键入任何命令行,我将尽力执行它。

那危险吗?每个人都应该清楚。

eval的安全规则应为:
仅对已为其赋值的变量执行eval 。

在这里阅读更多细节


10

eval最为解释型语言(的功能TCLpythonruby...),不仅炮弹。它用于动态评估代码。

在shell中,它作为shell内置命令实现。

基本上,eval将字符串作为参数并评估/解释其中的代码。在shell中,eval可以采用多个参数,但是eval只是将它们串联起来以形成要评估的字符串。

它非常强大,因为您可以动态构造代码并运行它,而在C等编译语言中则无法做到这一点。

喜欢:

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

但这也很危险,因为对传递给eval它的动态(外部提供的)部分进行清理非常重要,因为正因为它被解释为shell代码。

例如,如果以上$1evil-command; vareval将结束向上评估所述evil-command; var=$varvalue壳代码然后运行evil-command

邪恶eval经常被夸大。

好的,这很危险,但是至少我们知道这很危险。

很多其他的命令将在其参数评估shell代码如果不消毒,(取决于外壳),像[又名testexportprintf,GNU sedawk,当然sh/ bash/ perl和所有解释...

实施例(在此使用uname作为evil-command$a作为外部提供的数据unsanitized):

$ 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

这些sedexport...命令可以因为虽然它显然认为更危险eval "$var"会造成的内容$var作为shell代码进行评估,它不是那么明显与sed "s/foo/$var/"export "$var=value"[ "$var" -gt 0 ]。危险性是相同的,但是在其他命令中却隐藏了。


@BinaryZebra,sed就像eval正在传递一个包含在变量中的字符串一样,对于这两种字符串,该字符串的内容最终都将作为shell代码进行评估,因此与之一样sed危险eval,这就是我要说的。sed正在传递包含uname(到目前为止尚未执行uname)的字符串,并且通过调用sed,uname命令最终被执行。喜欢评估。在中sed 's/foo/$a/g',您没有将未经消毒的数据传递给sed,这不是我们在这里谈论的。
斯特凡Chazelas

2

此示例可能会阐明一些问题:

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

上面脚本的输出:

$VAR2
$VAR1
25

感谢您的贡献,但是,尽管如此,我们仍然很有趣,因此我们并没有兴趣编写使用(略有不同)示例的列表eval。您是否认为eval现有答案未涵盖该功能的某些基本,关键方面?如果是这样,则对其进行解释,并使用示例来说明您的解释。请不要在评论中回复;编辑  您的答案,使其更清晰,更完整。
斯科特(Scott)
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.