eval 属 Shell 内建命令,通过连接参数构造命令。其使用空格分隔每个参数,构造的命令应由 Shell 读取和执行。
eval 会对后面的命令进行两遍扫描。如果第一遍扫描后,命令是个普通命令,则执行此命令;如果命令中含有变量的间接引用,则保证间接引用的语义。也就是说,eval 命令将会首先扫描命令行进行所有的置换,然后再执行该命令。
命令格式:
eval [argument...]
使用示例
# eval $(cat cmd.txt)
以上命令读取文本文档cmd.txt中的内容,并将内容作为命令行来执行。
例如cmd.txt的内容如下:
# cat cmd.txt
echo “Hello World!”
执行结果为:
# eval $(cat cmd.txt)
Hello World!
非常直观。
在一项业务需求中,我会将若干文件移动命令写入文本,然后提交eval执行,例如:
# cat cmd.txt
mv “a.jpg” “a_1.jpg” && mv “b.jpg” “b_1.jpg”
相当长的时间内,该业务运行正常。直至今日,命令无法执行成功,提示:文件不存在!
经检查,文件明明就在那里。
调试后发现,原来是空格惹的祸。从上面的示例可以看到,我已经提前考虑到了文件名包含空格的情况,对文件名使用双引号包围,以避免文件名被分割为多个选项或参数。因此,该业务能够在文件名包含空格的情况下正常运行,但仅限于文件名中不存在连续空格的情况,一旦文件名中存在连续空格,则命令无法执行成功。
# cat cmd.txt
mv “a c.jpg” “a c_1.jpg” && mv “b.jpg” “b_1.jpg”
对于以上命令,将无法执行成功,提示找不到文件:a c.jpg。
在文件名中的连续空格较多的情况下,容易找到问题所在,当连续空格仅有两个时,需要非常仔细地观察才能发现问题。
经过测试后发现,eval命令的解析其参数内容,比如cmd.txt文件中的内容时,会将字符串中的连续空格合并为单一空格,即使字符串使用了双引号来包围,例如:
# cat cmd.txt
echo “Hello World!”
执行结果为:
# eval $(cat cmd.txt)
Hello World!
因此,请慎用eval。对于本例,替代方案很简单,直接调用bash执行文本文件即可:
# /bin/bash cmd.txt
Hello World!