还是回到我们的command line来吧...
经过前面两章的学习,应该很清楚当你在shell prompt后面敲打键盘、直到按下Enter的时候,你输入的文字就是command line了,然后shell才会以行程的方式执行你所交给它的命令。但是,你又可知道:你在command line输入的每一个文字,对shell来说,是有类别之分的呢?
简单而言(我不敢说这是精确的定议,注一),command line的每一个charactor,分为如下两种:
* literal:也就是普通纯文字,对shell来说没特殊功能。
* meta:对shell来说,具有特定功能的特殊保留字符。
(注一:关于bash shell在处理command line时的顺序说明,请参考O'Reilly出版社之Learning the Bash Shell, 2nd Edition,第177 - 180页的说明,尤其是178页的流程图Figure 7-1 ... )
Literal没甚么好谈的,凡举abcd、123456这些"文字"都是literal ... (easy?)
但meta却常使我们困惑..... (confused?)
事实上,前两章我们在command line中已碰到两个几乎每次都会碰到的meta:
* IFS:由或或三者之一组成(我们常用space )。
* CR:由产生。
IFS是用来拆解command line的每一个词(word)用的,因为shell command line是按词来处理的。而CR则是用来结束command line用的,这也是为何我们敲命令就会跑的原因。除了IFS与CR,常用的meta还有:
=:设定变量。
$:作变量或运算替换(请不要与shell prompt搞混了)。
>:重导向(重定向)stdout。
<:重导向(重定向)stdin。
|:命令管线(管道)。
&:重导向file desCRiptor,或将命令置于背境(后台)执行。
( ):将其内的命令置于nested subshell执行,或用于运算或命令替换。
{ }:将其内的命令置于non-named function中执行,或用在变量替换的界定范围。
;:在前一个命令结束时,而忽略其返回值,继续执行下一个命令。
&&:在前一个命令结束时,若返回值为true,继续执行下一个命令。
||:在前一个命令结束时,若返回值为false,继续执行下一个命令。
!:执行history列表中的命令
...
假如我们需要在command line中将这些保留字符的功能关闭的话,就需要quoting处理了。
在bash中,常用的quoting有如下三种方法:
* hard quote:' ' (单引号),凡在hard quote中的所有meta均被关闭。
* soft quote:" " (双引号),在soft quoe中大部份meta都会被关闭,但某些则保留(如$,反引号,反斜杠)。
* escape:\(反斜线),只有紧接在escape (跳脱字符)之后的单一meta才被关闭。
下面的例子将有助于我们对quoting的了解:
$ A=B C #空格键未被关掉,作为IFS处理。
$ C: command not found.
$ echo $A
$ A="B C" #空格键已被关掉,仅作为空格键处理。
$ echo $A
B C
在第一次设定A变量时,由于空格键没被关闭,command line将被解读为:
* A=B然后碰到<IFS>,再执行C命令在第二次设定A变量时,由于空格键被置于soft quote中,因此被关闭,不再作为IFS:
* A=BC事实上,空格键无论在soft quote还是在hard quote中,均会被关闭。Enter键亦然:
$ A='B > C > ' $ echo "$A" B C |
4) " "(双引号) 与 ' '(单引号)差在哪?
还是回到我们的 command line 来吧...
经过前面两章的学习,应该很清楚当你在 shell prompt 后面敲打键盘、直到按下 Enter 的时候,你输入的文字就是 command line 了,然后 shell 才会以行程的方式执行你所交给它的命令。但是,你又可知道:你在 command line 输入的每一个文字,对 shell 来说,是有类别之分的呢?简单而言(我不敢说这是精确的定议,注一),command line 的每一个charactor ,分为如下两种:
* literal:也就是普通纯文字,对 shell 来说没特殊功能。
* meta:对 shell 来说,具有特定功能的特殊保留字符。
(注一:关于 bash shell 在处理 command line 时的顺序说明,请参考 O'Reilly 出版社之 Learning the Bash Shell, 2nd Edition,第 177- 180 页的说明,尤其是 178 页的流程图 Figure 7-1 ... ) Literal 没甚么好谈的,凡举 abcd、123456 这些"文字"都是 literal ...(easy?)但 meta 却常使我们困惑..... (confused?)事实上,前两章我们在 command line 中已碰到两个机乎每次都会碰到的 meta:
* IFS:由 或 或 三者之一组成(我们常用space )。
* CR:由 产生。
IFS 是用来拆解 command line 的每一个词(word)用的,因为 shell command line 是按词来处理的。而 CR 则是用来结束 command line 用的,这也是为何我们敲 命令就会跑的原因。除了 IFS 与 CR ,常用的 meta 还有:
= : 设定变量。
$ : 作变量或运算替换(请不要与 shell prompt 搞混了)。
> :重导向 stdout。
< :重导向 stdin。
|:命令管线。
& :重导向 file descriptor ,或将命令置于背境执行。
( ):将其内的命令置于 nested subshell 执行,或用于运算或命令替换。
{ }:将其内的命令置于 non-named function 中执行,或用在变量替换的界定范围。
; :在前一个命令结束时,而忽略其返回值,继续执行下一个命令。
&& :在前一个命令结束时,若返回值为 true,继续执行下一个命令。
|| :在前一个命令结束时,若返回值为 false,继续执行下一个命令。
!:执行 history 列表中的命令
....
假如我们需要在 command line 中将这些保留字符的功能关闭的话,就需要quoting 处理了。在 bash 中,常用的 quoting 有如下三种方法:
* hard quote:' ' (单引号),凡在 hard quote 中的所有 meta 均被关闭。
* soft quote: " " (双引号),在 soft quoe 中大部份 meta 都会被关闭,但某些则保留(如 $ )。(注二)
* escape : \ (反斜线),只有紧接在 escape (跳脱字符)之后的单一 meta才被关闭。
( 注二:在 soft quote 中被豁免的具体 meta 清单,我不完全知道,有待大家补充,或透过实作来发现及理解。 )
下面的例子将有助于我们对 quoting 的了解:
代码:
$ A=B C # 空格键未被关掉,作为 IFS 处理。
$ C: command not found.
$ echo $A
$ A="B C" # 空格键已被关掉,仅作为空格键处理。
$ echo $A
B C
在第一次设定 A 变量时,由于空格键没被关闭,command line 将被解读为:
* A=B 然后碰到,再执行 C 命令
在第二次设定 A 变量时,由于空格键被置于 soft quote 中,因此被关闭,不再作为 IFS :
* A=BC
事实上,空格键无论在 soft quote 还是在 hard quote 中,均会被关闭。
Enter 键亦然:
代码:
$ A='B
> C
> '
$ echo "$A"
B
C
在上例中,由于 被置于 hard quote 当中,因此不再作为 CR 字符来处理。这里的 单纯只是一个断行符号(new-line)而已,由于 command line并没得到 CR 字符,因此进入第二个 shell prompt (PS2,以 > 符号表示),command line 并不会结束,直到第三行,我们输入的 并不在 hard quote 里面,因此并没被关闭,此时,command line 碰到 CR 字符,于是结束、交给 shell 来处理。上例的 要是被置于 soft quote 中的话, CR 也会同样被关闭:
代码:
$ A="B
> C
> "
$ echo $A
B C
然而,由于 echo $A 时的变量没至于 soft quote 中,因此当变量替换完成后并作命令行重组时, 会被解释为 IFS ,而不是解释为 New Line 字符。同样的,用 escape 亦可关闭 CR 字符:
代码:
$ A=B\
> C\
>
$ echo $A
BC
上例中,第一个 跟第二个 均被 escape 字符关闭了,因此也不作为 CR 来处理,但第三个 由于没被跳脱,因此作为 CR 结束 command line 。但由于 键本身在 shell meta 中的特殊性,在 \ 跳脱后面,仅仅取消其 CR 功能,而不会保留其 IFS 功能。您或许发现光是一个 键所产生的字符就有可能是如下这些可能:
CR
IFS
NL(New Line)
FF(Form Feed)
NULL
...
至于甚么时候会解释为甚么字符,这个我就没去深挖了,或是留给读者诸君自行慢慢摸索了... ^_^
至于 soft quote 跟 hard quote 的不同,主要是对于某些 meta 的关闭与否,以 $ 来作说明:
代码:
$ A=B\ C
$ echo "$A"
B C
$ echo '$A'
$A
在第一个 echo 命令行中,$ 被置于 soft quote 中,将不被关闭,因此继续处理变量替换,因此 echo 将 A 的变量值输出到荧幕,也就得到 "B C" 的结果。在第二个 echo 命令行中,$ 被置于 hard quote 中,则被关闭,因此 $ 只是一个 $ 符号,并不会用来作变量替换处理,因此结果是 $ 符号后面接一个 A 字母:$A 。
--------------------------------------
练习与思考:如下结果为何不同?
代码:
$ A=B\ C
$ echo '"$A"' # 最外面的是单引号
"$A"
$ echo "'$A'" # 最外面的是双引号
'B C'
(提示:单引号及双引号,在 quoting 中均被关?#93;了。)
--------------------------------------
在 CU 的 shell 版里,我发现有很多初学者的问题,都与 quoting 理解的有关。比方说,若我们在 awk 或 sed 的命令参数中调用之前设定的一些变量时,常会问及为何不能的问题。要解决这些问题,关键点就是:
* 区分出 shell meta 与 command meta
前面我们提到的那些 meta ,都是在 command line 中有特殊用途的,比方说 { } 是将其内一系列 mmand line 置于不具名的函式中执行(可简单视为 command block ),但是,awk 却需要用 { } 来区分出 awk 的命令区段(BEGIN, MAIN, END)。若你在 command line 中如此输入:
代码:
$ awk {print $0} 1.txt
由于 { } 在 shell 中并没关闭,那 shell 就将 {print $0} 视为 command block ,但同时又没有" ; "符号作命令区隔,因此就出现 awk 的语法错误结果。要解决之,可用 hard quote :
代码:
$ awk '{print $0}' 1.txt
上面的 hard quote 应好理解,就是将原本的 {、、$(注三)、} 这几个shell meta 关闭,避免掉在 shell 中遭到处理,而完整的成为 awk 参数中的 command meta 。
( 注三:而其中的 $0 是 awk 内建的 field number ,而非 awk 的变量,awk 自身的变量无需使用 $ 。)
要是理解了 hard quote 的功能,再来理解 soft quote 与 escape 就不难:
代码:
awk "{print \$0}" 1.txt
awk \{print\ \$0\} 1.txt
然而,若你要改变 awk 的 $0 的 0 值是从另一个 shell 变量读进呢?
比方说:已有变量 $A 的值是 0 ,那如何在 command line 中解决 awk 的$$A 呢?
你可以很直接否定掉 hard quoe 的方案:
代码:
$ awk '{print $$A}' 1.txt
那是因为 $A 的 $ 在 hard quote 中是不能替换变量的。
聪明的读者(如你!),经过本章学习,我想,应该可以解释为何我们可以使用如下操作了吧:
代码:
A=0
awk "{print \$$A}" 1.txt
awk \{print\ \$$A\} 1.txt
awk '{print $'$A'}' 1.txt
awk '{print $'"$A"'}' 1.txt # 注:"$A" 包在 soft quote 中或许,你能举出更多的方案呢.... ^_^
上面的 IFS=; 其实就是:
IFS=
将值设为 null
然后补一个" ; " 的 meta ...
昨晚的文章,一开始就提到 IFS 的作用了:
是 breaks command line down to each word 用的...
而 command line 则是 processes word by word 进行的,
若碰到 meta 或 key words ,会先处理之,等到所有的 words 都处理完毕,
那才开始传给 shell 处理...
man bash 在 Special Parameters 那节有提到:
代码:
* Expands to the positional parameters, starting from
one. When the expansion occurs within double
quotes, it expands to a single word with the value
of each parameter separated by the first character
of the IFS special variable. That is, "$*" is
equivalent to "$1c$2c...", where c is the first
character of the value of the IFS variable. If IFS
is unset, the parameters are separated by spaces.
If IFS is null, the parameters are joined without
intervening separators.
请理解最后一句的意思,你就能理解 IFS= 的意思...
IFS=; 其实就是 IFS=
请记着﹗
当 IFS 为 null 时,在 $* (也就是 positional parameter)将被视为单一的
word ,因为跟本没区隔嘛(想想 IFS 作啥用?)....
a=" abc"
之后,若 IFS 没改变的话,那下面两行是有差别的:
echo $a
echo "$a"
差别就在 quoting ! (自己再想想 space 在 soft quote 是否为 IFS ?)
若 IFS=
再跑上两行 echo ,那就没差了,
因为不管 space 在 quoting 内还是外,都不是 IFS ﹗
阅读(1647) | 评论(0) | 转发(0) |