eval 使用详解

每次碰到 eval 命令都感觉似懂非懂。今天准备好好梳理梳理。

eval 命令的用法与功能

使用模式:eval [argument …]

功能描述:通过将给定的参数以一个空格为分隔符连接在一起构造出一条命令。构造出的命令会被 shell 读取并执行。

退出状态:如果没有参数,或者只有 null 参数,eval 将会返回退出状态(exit status)0;否则,它会返回 构造出的命令的 退出状态。

好像还是没什么感觉。来看看官方给的例子吧:

1
2
3
4
5
6
7
> foo=10 x=foo
> y='$'$x
> echo $y
$foo
> eval y='$'$x
> echo $y
10

  1. 第一行定义了 \$foo, \$x,其值分别为 ‘10’, ‘foo’。注意,\$variable_name 表示 shell 变量!
  2. 第二行定义了 \$y,其值由字面值 ‘\$’ 后接 \$x 的值组成,即字符串 ‘\$foo’。使用英文单引号对美元符号进行转义。
  3. 查看 \$y 的值,echo \$y。输出结果:\$foo
  4. 现在使用 eval 命令重新对变量 y 赋值。它将先对 \$x 求值得到字符串 ‘foo’。现在赋值语句变成了 y=\$foo,再次求值,得到 y=10。
  5. 查看 \$y 的值,验证结果

貌似 eval 会将传入的参数作为 shell 语句来执行,所以才不会把赋值操作符(=)右边的部分视为字符串值,而是视为 shell 表达式,并对其中每一部分求值。感觉非常像 javascript eval(),该函数对 string 形式的 javascript code 求值。

现在,对 eval 已经有了比较直观的了解。但还不够,我很想知道给 eval 传入多个参数会发生什么神奇的事情。

eval 深度历险

eval 功能描述中有一点值得注意,它会执行 参数拼接在一起构成的命令。做个简单的试验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> player='科比'
> eval echo $player
科比
> player2='$'player
> echo $player2
$player
> eval echo $player2
科比

# 第 6 条
> eval echo$player
command not found: echo科比
# 第 7 条
> eval echo '$player2'
$player
# 第 8 条
> echo '$player2'
$player2
# 第 9 条
> eval echo "$player2"
科比

结合官方实例和上面的试验来分析哈 eval 命令的执行过程:

  1. 首先对每一个输入参数进行求值处理:

    1.1 如果参数是一个赋值语句,就会对 = 号右边的表达式递归求值,直到得出最终值,请看官方示例的分析。

    1.2 如果参数非赋值语句,只是包含形如 \$xxx 的表达式,就对这些表达式求值(使用变量值替换表达式),仅求值一次

    第 6、7、9 条命令的执行结果可以佐证这一点。对于 echo 命令来说,单引号中的内容不做求值处理,原样输出,请看第 8 条命令;而 eval 命令不管参数是否被单双引号包裹,一律进行一次求值处理,请看第 6、7 条。

    接下来你肯定会问,第 9 条命令明明进行了两次求值嘛,先对 \$player2 求值得到 \$player,再对 \$player 得到字符串 ‘科比’,为什么?

    因为第一次求值是 eval 命令的作用,第二次求值是 echo 命令的作用。echo 命令处理参数的行为是,如果参数被单引号包裹,就原样输出;如果参数被双引号包裹,先对其中包含的 \$xxx 表达式求值替换再输出。

  2. 参数处理完毕后,就把所有参数以空格为分隔符连接起来,作为一条命令去执行。

为了让上面的推论更加靠谱,下面使用 sh -v 在每条命令执行前秀出它们的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> cat test.sh
#!/bin/bash

player='kobe'
player2='$'player
eval echo $player2

> sh -v test.sh
#!/bin/bash

player='kobe'
player2='$'player
eval echo $player2
echo $player
kobe

看到木有,实际执行的 echo 命令是 echo $player 而非 echo 'kobe'

好了,感觉妈妈再也不用担心我碰到 eval 命令了。

参考资源

  1. eval man page,你可以该页面上搜索任何命令在各种 linux 系统上的用法。
  2. What is the “eval” command in bash?
0%