shell脚本学习指南[三](Arnold Robbins & Nelson H.F. Beebe著)

2019-09-23 09:42:11王冬梅

2、检查每个命令的第一个token,看看是否它是不带有引号或反斜杠的关键字。如果它是一个开放的关键字(if 或者 { (之类的),则这个命令其实是一个复合命令。shell为复合命令进行内部的设置,读取下一条命令,并再次启动进程。如果关键字非复合命令的开始符号(例如,它是控制结构的中间部分,像then、else或do,或是结尾部分,例如fi,done或逻辑运算符),则shell会发出语法错误的信号。
3、将每个命令的第一个单词与别名列表对照检查。如果匹配,它便代替别名的定义,并回到步骤1;否则,进行步骤4(别名是给交互式shell使用)。回到步骤1,允许让关键字的别名被定义:例如alias aslongas=while or alias procedure=function。注意,shell不会执行递归的别名展开,反而当别名展开为相同的命令时它会知道,并停止潜在的递归操作。可以通过引用要保护的单词的任何部分而禁止别名展开。
4、如果波浪号字符出现在单词的开头处,则将 波浪号替换成用户的跟目录$HOME,将~user替换成user的根目录。
波浪号替换会发生在下面的位置:
* 在命令行里,作为单词的第一个未引用字符
* 在变量赋值中的=之后以及变量赋值中的任何:之后
* 形式${ varibale op word } 的变量替换里的word部分
5、将任何开头为$符号的表达式,执行参数(变量)替换。
6、将任何形式为$(string)或 `string`的表达式,执行命令替换。
7、执行形式$((string))的算术表达式。
8、从参数、命令与算术替换中取出结果行的部分,再一次将它们切分为单词。这次它使用$IFS里的字符作为定界符,而不是使用步骤1的那组meta字符。通常,在IFS里连续多个重复的输入字符是作为单一定界符,这是你所期待的。这只有对空白字符而言是真的。对非空白字符,则不是这样的。举例说,当读取以冒号分隔字段的/etc/passwd文件时,两个连续冒号所界定的是一个空子段。
9、对于*、?以及一对[...]的任何出现次数,都执行文件名生成的操作,也就是通配符展开。
10、使用第一个单词作为一个命令,遵循查找次序,也就是,先作为个特殊的内建命令,接着是作为函数,然后作为一般的内建命令,以及最后作为查找$PATH找到的第一个文件。
11、在完成I/O重定向与其他同类型事项之后,执行命令。

shell程序碰到一句命令,都会执行上边的一次流程。比如:

$ mkdir /tmp/x
$ cd /tmp/x
$ touch f1 f2
$ f=f y="a b"
$ echo ~+/${f}[12] $y $(echo cmd subst) $((3+2)) > out

命令一开始会根据shell语法分割token,最重要一点是I/O重定向 > out 在这里是被识别的,并存储供稍后使用。最后这句echo被分为5个token,分别是: echo ,~+/${f}[12] , $y , $(echo cmd subst) ,$((3 + 2))这5个部分。