话说大家都知道,shell中的变量是等价替换的。例如下面这个例子:
上面这个结果就是表示给变量cmd赋值为ls -lrt ,然后直接使用变量就相当于执行了ls -lrt
因此在shell脚本中当然也可以这么写,例如:
<span style="font-family:Microsoft YaHei;font-size:12px;">#!/bin/bash
function main()
{
cmd="pwd"
$cmd
}
main $@
</span>
运行结果:
su - jeremysong -c "(pwd;. /media/jeremysong/document/test/source.sh;ls -lrt)"
它的直接结果如下:
然后我们写两个脚本,source.sh和runner.sh
<span style="font-family:Microsoft YaHei;font-size:12px;">#!/bin/bash
export DEVICE="Thinkpad SL410"
export OS="Ubuntu"
function print_time()
{
date
}
function main()
{
print_time
}
main $@
</span>
<span style="font-family:Microsoft YaHei;font-size:12px;">#!/bin/bash
function run()
{
echo "------------------runner-----------START-------------"
cmd="su - jeremysong -c \"(pwd;. /media/jeremysong/document/test/source.sh;ls -lrt)\""
${cmd}
echo "------------------runner------------END--------------"
}
run $@
</span>
纳尼?! 为什么报错了?shell不是变量替换么,单独运行哪一行一点问题都没有,这是为什么?好,那么我们用调试模式看一下:
好吧,我们现在确认了是上面圈出来的那句出错了,那么这个能否正常执行呢?
答案肯定是不行的。
因为shell中的双引号和单引号虽然都可以表示字符串,但是两者还有差别的。
单引号用于保持引号内所有字符的字面值,即使引号内的\和回车也不例外,但是字符串中不能出现单引号。(注意是所有,只是单引号本身不能够出现在其中)。
双引号用于保持引号内所有字符的字面值(回车也不例外),但以下情况除外:
-
$加变量名可以取变量的值
-
反引号仍表示命令替换
-
\$表示$的字面值
-
\`表示`的字面值
-
\"表示"的字面值
-
\\表示\的字面值
-
除以上情况之外,在其它字符前面的\无特殊含义,只表示字面值。
下面看个例子:
可见,单引号中完全是保持字符串的原型输出,而双引号进行了命令替换。
所以我们可以修改runner.sh脚本成如下的写法:
<span style="font-family:Microsoft YaHei;font-size:12px;">#!/bin/bash
function run()
{
echo "------------------runner-----------START-------------"
cmd="su - jeremysong -c \"(pwd;. /media/jeremysong/document/test/source.sh;ls -lrt)\""
eval $cmd
echo "------------------runner------------END--------------"
}
run $@
</span>
我们在看看运行结果:
运行结果完全没问题。那么为何eval命令可以解决这个问题呢?
答案是,eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,就像我们刚才那样写的例子。
eval命令对变量进行两次扫描。这些需要进行两次扫描的变量有时被称为复杂变量。不过这些变量本身并不复杂。eval命令也可以用于回显简单变量,不一定是复杂变量。
那么,好了。这里要说的也就是怎么来处理问题了。如果读者对于shell中的单引号和双引号之间的差异,以及eval命令想要更进一步了解的话,可以多查查资料熟悉熟悉,这里就不一一介绍了。
知识点:
- shell中单引号和双引号的区别
- shell脚本的调试
- shell命令eval