echo命令是一个看似简单的命令,当它和转义,命令替换等相遇时,则会发生许多令人迷惑的现象。
1.echo \z和echo “\z”的不同
echo命令比较特殊,对于它本身而言,转义符加上某些字符会引发一些特殊含义,这与转义本身的含义又相反。比如\n在echo中表示回车换行,\t表示水平制表符等。不过,echo命令在bash中默认是不开启对这些特殊转义符进行解释的,如果要开启则必须加上-e选项。
对于下述的命令:
$ echo -e "\\thello" hello
它的执行过程是:
1).”\\thello”首先被bash解释为”\thello”。
2).”\thello”作为echo的参数,由于加入了-e,因此echo将\t解释为水平制表符。
3).显示结果。
上述命令发生了两次转义,第一即为执行过程1中bash处理命令行时,第二次即为2中echo处理自身参数时。
对于弱引用而言,双引号中的所有字符都被当做普通字符,除了\,$和反引号(本文所述的反引号就主键盘上数字1左边的符号)。
在对字符串不加引用的情况下,\会发挥其转义的特性,即显示字符字面含义,即便该字符是普通字符,因此echo \z的结果为z。而双引号中所有字符都被解释成shell解释成了普通字符,”\z”被解释的结果为\z,并且\z并不是echo的特定转义符号,因此echo “\z”的结果为\z。
edsionte@edsionte-laptop:~/mytest$ echo \z + echo z z edsionte@edsionte-laptop:~/mytest$ echo "\z" + echo '\z' \z
这里你也许有疑问:弱引用是不能关闭$,\和反引号的特殊含义的,为何echo “\z”中的\没有起到转义的作用呢?
这里需要注意的是,弱引用中\符号仅对$,\,反引号和双引号这几个特殊字符起转义作用,而其他字符前的\均不起作用,也就是说shell会忽略它,将这个\作为普通字符。
edsionte@edsionte-laptop:~/mytest$ t=a + t=a edsionte@edsionte-laptop:~/mytest$ echo "\$t" + echo '$t' $t edsionte@edsionte-laptop:~/mytest$ echo "\*t" + echo '\*t' \*t
通过上述的分析,也就简单解释了为何echo \z和echo “\z”会有不同的结果。
2.echo `echo \z`的疑惑
接下来要说明的不是上述一条命令,而是关于它的“一系列”命令。
$ cat test2echo.sh #!/bin/bash echo `echo \z` echo `echo \\z` echo `echo \\\z` echo `echo \\\\z` echo `echo \\\\\\z` echo `echo \\\\\\\z` $ bash test2echo.sh z z \z \z \z \\z
这几条echo命令中均嵌套了另一个echo命令,命令替换使用的是反引号而不是$()。从上面的结果可以看出当echo遇到\时发生了一些奇怪的现象,比如为何第3,4,5条echo命令的结果是一致的。
在解释上述这几个echo命令之前,我们先看下面的脚本:
$ cat testecho.sh #!/bin/bash echo \z echo \\z echo \\\z echo \\\\z echo \\\\\\z echo \\\\\\\z $ bash testecho.sh z \z \z \\z \\\z \\\z
上面的脚本的结果是比较容易想到的。第一条echo中的\由于对z进行转义;第二条echo中的第一个\对第二个\进行转义;第三条echo中的第一个\对第二个\进行转义,第三个\对z进行转义;后续的echo命令的规则都是一致的。
如果将testecho.sh中的结果作为test2echo.sh中每个内层echo的输出,那么会不会得到test2echo.sh最终的结果呢?比如echo `echo \\\z`,内部echo的结果是\z,那么这个双层echo可以看作是echo \z。按照上述1中的讨论,结果应该是z。看来处理过程并不是如此。
那么echo `echo \\\z`是否可以看作是echo “\z”?根据上述1中的讨论结果,结果正确。不过对于echo `echo \\\\z`,按照这样的处理并不成立。
接下来我们使用set命令来追踪test2echo.sh的执行过程:
$ cat test2echo.sh #!/bin/bash set -x echo `echo \z` echo `echo \\z` echo `echo \\\z` echo `echo \\\\z` echo `echo \\\\\\z` echo `echo \\\\\\\z` $ bash test2echo.sh ++ echo z + echo z z ++ echo z + echo z z ++ echo '\z' + echo '\z' \z ++ echo '\z' + echo '\z' \z ++ echo '\z' + echo '\z' \z ++ echo '\\z' + echo '\\z' \\z
通过加入-x选项我们可以发现内部echo的输出结果被单引号所引用,因此可以得知上述双层echo命令的结果都是内部echo的输出结果。因为每个内部echo的输出都通过单引号原封不动的作为外部echo的输入,并且单引号中的所有字符都是普通字符(除了单引号本身)。那么究竟是什么原因才导致这样令人迷惑的结果?
在上述问题1中我们已经说明过双引号中\对$,反引号,双引号和\起到转义作用,即显示这些字符的字面意思,那么这几个特殊字符前的\也将抽离所在字符串。同样,反引号中也存在这样的问题,如果反引号中有\,并且\后紧跟的字符是$,反引号,双引号和\这些字符时,那么这些特殊字符前的\将抽离。
按照上述的解释,test2echo.sh中各个echo的执行过程就明了了。以echo `echo \\\z`为例,它的执行过程如下:
1).\\\z由于反引号中的\前出现了\,因此第二个\被抽离,原始命令成为echo `echo \\z`。
2).执行反引号中的echo,原始命令成为echo ‘\z’
3).执行外部echo,结果为\z。
再举一例,比如上述的echo `echo \\\\\\\z`,它的执行过程如下:
1).内部\\\\\\\z由于反引号中的\前又出现了\,因此有三个\被抽离,原始命令成为echo `echo \\\\z`。
2).执行反引号中的echo,原始命令成为echo ‘\\z’
3).执行外部echo,结果为\\z。
其他命令的执行过程也是如此。每个命令中的反引号部分都先经过\的抽离,接着bash对字符串进行转移处理,再输入内部echo;内部echo处理完成后,传递给外部echo,最终输出结果。上述过程中的2)和3)均可以通过set -x来追踪。