eval 是 shell 内建的一个命令。
它的主要作用是:
下面一段代码包含了 eval 的三种常见用法。
ID=5
# 给变量赋值
eval TEST_CASE_${ID}=1005
# 获取该变量名
TEST_CASE_NAME=$(eval echo TEST_CASE_${ID})
echo $TEST_CASE_NAME # print TEST_CASE_5
# 打印该变量值
eval VAL=\$${TEST_CASE_NAME}
echo $VAL # print 1005
就是这句 eval TEST_CASE_${ID}=1005
这里的第1次解析,就是把${ID}
替换成5;
这里的第2次解析,就是把第一次解析的结果 TEST_CASE_5=1005
交给shell去执行
也就是,我们想知道 TEST_CASE_${ID}
到底是什么,该怎么做呢?
答案就是这句 $(eval echo TEST_CASE_${ID})
.
(当然在上面的代码中,是把它赋给一个变量,方便打印。)
这里的第1次解析,还是把${ID}
替换成5;
这里的第2次解析,就是把第一次的解析结果 echo TEST_CASE_5
交给shell去执行。
在上面的代码中,我们已知变量名为 ${TEST_CASE_NAME}
,即TEST_CASE_5
.
然后我们想知道 ${TEST_CASE_5}
到底是什么。
这个时候,就会用到这句 eval VAL=\$${TEST_CASE_NAME}
这里的第1次解析,就是把${TEST_CASE_NAME}
替换成 TEST_CASE_5
这里的第2次解析,就是把第一次的解析结果 VAL=$TEST_CASE_5
交给shell去执行(可能由于shell的某些解析限制,需要在第一个美元符号前面加一个反斜杠)
其实这里还有第二种写法(本质上和第一种写法相同)
VAL="$(eval echo \$${TEST_CASE_NAME})"
echo $VAL
这种写法的第1次解析,仍是把${TEST_CASE_NAME}
替换成 TEST_CASE_5
这种写法的第2次解析,就是把第一次的解析结果 echo ${TEST_CASE_5}
交给shell去执行
以上的第3种用法其实经常被需要用到,即:已知变量名,求变量值。
再来看一个例子:
VAR=100
VAR_NAME=VAR
eval value=\$${VAR_NAME}
echo $value
以上会打印100.
除了上述的3种常见用法外,还有第4种常见用法,即:
比如,command="cat ./myfile.txt"
, 这个command是一个字符串,怎么把它当命令执行呢?
直接交给 eval 就可以了,如下
$ echo "This is my file" > myfile.txt
$ cat myfile.txt
This is my file
$ CMD="cat ./myfile.txt"
$ eval $CMD
This is my file
第1次解析,把变量$CMD
替换成它所代表的字符串;
第2次解析,把这个字符串交给shell去执行。
eval 最常见的用法大约就是以上4种了。
为什么eval是危险的呢?就是因为它可能会执行一些被恶意填入字符串中的命令。
举个栗子:
#!/bin/sh
fifth() {
_fifth_array=$1
eval echo "\"The fifth element is \${$_fifth_array[4]}\"" # DANGER!
}
a=(zero one two three four five)
fifth a
fifth 'x}"; date; #'
以上代码的执行结果如下:
The fifth element is four
The fifth element is
Sat Sep 9 14:24:19 UTC 2023
这哪里危险了?它执行了一个用户作为参数输入的命令 - date, 如果用户输入的是 “sudo rm -rf /” 呢?
为什么会执行到date呢?
本来 fifth() 函数的参数是一个数组,但是有人恶意填了一个字符串。此时,第一次解析就用这个字符串去代入了。
经过第一次解析,传递给shell的是这么一个东西:
echo "The fifth element is ${x}"; date; #[4]}"
所以,代入之后,
最后一个参考文献中还有更复杂的危险case,就不再列出了。有兴趣的读者可自行参阅。
(END)