shell历险之——引用的迷途
上篇:引用,奇怪的反斜线 我们知道在shell中有两类字符,一类是普通字符(literal),在shell中没有任何特殊意义;另一类是所谓“元字符”(meta),在shell中有特殊的含义或用法。 当我们需要去掉元字符的特殊含义而恢复其字面意义时就必须使用“引用”(quoting)。通常有三种引用方式,他们是转义(Escape,使用反斜杠字符\,即backslash),强引用(使用单引号',即single quote)和弱引用(使用双引号", 即double quote)。 转义:是用反斜杠放在需要转义的一个字符前,表示那个字符要看作一个普通字符。 强引用:是用单引号把要转义的字符串括起来,其中任何字符都看作普通字符,除了单引号自身。所以你无法在两个单引号之间包含单引号,用\转义也不行。 弱引用:是用双引号把要转义的字符串括起来,除了双引号"本身,其中的大部分字符都看作普通字符。例外的还有\,$,`三个特殊字符。因为\在""中是特殊字符,所以你可以在其中包含"本身,前提是必须转义。$是特殊字符,这表示你可以使用变量$var/${var}及其它,在ksh/bash中可以引用算术表达式的结果$((...)),还可以作命令替换$()。由于`是特殊字符在bsh中也可以作命令替换,但只能使用`...`的语法(这个在ksh/bash中也可以使用)。 关于三种引用,网中人的“shell十三问”之第四问已经讲得很清楚了,我这里就不重复了。 今天我们单独研究一下反斜线\。这是一个有魔力的字符,它可以用来对任何字符转义,也包括它自己。但是在不同的shell实现中它的表现似乎不尽相同,有时结果让你会大吃一惊。 先来看一个简单的例子,假定我们要输出单独一个\,先用bash:
CODE:
$ echo \\
\ $ echo "\\" \ $ echo '\' \ 第一个echo,因为\是元字符,所以必须对它进行转义,所以我们必须用两个\。 第二个echo,因为""是弱引用,其中的\仍然是特殊字符,所以同样必须转义。 第三个echo,''是强引用,\在单引号之中是普通字符,这样就不用再转义了,所以只用一个\。 OK,假如我们要输出连续两个\,怎样呢?看一下:
CODE:
$ echo \\\\
\\ $ echo "\\\\" \\ $ echo '\\' \\ 嗯,好像没有什么奇怪的事发生,我们简单地使用双倍的\就搞定了。 那再让我们用ksh来试试来做同样的事:
CODE:
$ echo \\
\ $ echo "\\" \ $ echo '\' \ 这个容易,简单的重复罢了。再来两个\:
CODE:
$ echo \\\\
\ $ echo "\\\\" \ $ echo '\\' \ 等等,怎么搞的?为什么只输出了一个\? 再加一个\会如何?
CODE:
$ echo \\\\\
> \ $ echo \\\\\\ \\ $ echo "\\\\\" > \ $ echo "\\\\\\" \\ $ echo '\\' \\ 第一个echo没有立即执行,ksh给出了一个>(PS2提示符),等待我们继续输入,回车后echo仍然只输出了一个\。 第二个echo后面是六个\,呼~,这回终于输出两个\了。 双引号的情形也是类似,我们仍然需要六个\。 第五个echo,好在单引号的结果还算不错,只要两个\就行了。 你可以继续做这个试验,最后会发现在不用单引号时,我们需要2个或4个\输出1个\,需要6个或8个\输出2个\,10个或12个\来输出3个\,......真是又臭又长!
CODE:
输出 命令行需要\的个数
\ 2/4 \\ 6/8 \\\ 10/12 \\\\ 14/16 \\\\\ 18/20 ... ... n个\ 4n-2/4n 厌倦了吗?其实ksh下除了用',还有其它方法剪断这“懒婆娘的裹脚布”。窍门就在于echo命令的-E选项:
CODE:
$ echo -E \\
\ $ echo -E \\\\ \\ $ echo -E \\\\\\ \\\ 很好!这样与bash下的情况就一样了,-E选项是不是很神奇呀!为什么呢? 原来bash和ksh的echo命令缺省的表现是不同的。我们知道echo命令可以接受一些转义字符序列来表示特殊的字符,如\n表示换行,\a表示蜂鸣,\t表示水平制表符等等。显然在解释转义序列时\是一个特殊字符。 在bash下echo缺省的设置是不解释这些转义序列,为了告诉它解释转义序列我们必须使用-e选项。 而在ksh下echo的缺省设置就会解释这些转义序列,我们可以用-E选项让它不解释转义序列。所以ksh下在不使用-E选项时实际上会发生两次转义的过程,第一次发生在ksh处理命令行时,第二次发生在echo命令处理它的参数时。让我们看下面这个简单的例子:
CODE:
$ set -x
$ echo \\\\ + echo \\ \ 先用set -x让ksh显示命令行处理的结果,我们看到,第一个\转义第二个\,去掉它的特殊含义,同样第三个\转义第四个\去掉它的特殊含义,这样命令行处理完毕以后传给echo的参数是\\。echo然后将\\解释成了\并输出。于是我们只得到一个\。 bsh中echo的缺省表现与ksh中类似,解释转义序列,不过可惜无法关掉这一功能。:(我们还是可以使用外部命令来不解释转义序列原样输出,例如在linux下可以用 /bin/echo -E(可以省略)来做。 下来看一个从本论坛来的例子,提问者的意图是将dos格式的路径中的\变成\\,但他的shell好像工作得不好:
CODE:
$echo C:\\tmp | sed 's/\\/&\\/'
C: mp $echo 'C:\abc'|sed 's/\\/\\\\/' C:bc 在看过上面我们对引用和echo的讨论之后,您能为他解释一下其中的原因吗? 对了,这两条命令的语法都没有错,不过提问者使用的shell八成是ksh(也可能是bsh,但可能性较小),问题是出在echo上。在ksh下echo默认解释转义序列,所以命令行的 echo c:\\tmp部分先做命令行解释,\\变成\,于是执行: echo 'c:\tmp' 而\t是一个转义序列,它代表水平制表符,所以echo最后输出 c:<水平制表符>mp 同样 echo 'c:\abc' 会输出 c:<蜂鸣字符>bc 你应该会听到你的终端发出“嘟”的一声。 这下清楚了吧?echo的输出就是错误的,后面sed的替换根本就没有匹配执行,当然不会有正确的结果。 上面两个命令在bash下是正确的,为什么? 那在ksh下怎样修改让它正确工作呢?如果是bsh呢?改法可能不止一种,不过这个还是留给亲爱的读者您作为一个练习好了。 |
|