在我的博文《Linux Shell Scripting Cookbook》Linux常用命令笔记(三)备注【2】中曾提及 命令行的解析,今天看《A Practical Guide to Linux Commands, Editors, and Shell Programming》这本书的时候有讲到 shell 对命令行命令的处理流程,这里做个笔记。
- 读入命令后,bash对这行命令进行 history expansion 和 alias subsitution。
history expansion,比如键入"!!"后回车会进行历史命令扩展,将 "!!" 替换为最近一次执行的命令(用 history 命令可以查看曾经键入的命令)。alias substitution,比如root用户一般会在~/.bashrc 中设置 rm 命令的别名:alias rm='rm -i' (用 alias 可以查看现有的命令别名)。
- 扫描和解析命令行
此时bash会将输入的一行命令解析成一个一个的 token。解析完成后,在执行命令之前,还需要遍历 tokens,完成 command-line expansion。
- BASH扫描所有解析的tokens,按顺序执行以下几种 expansion 或者 substitution。
- 1) Brace expansion
-
括号扩展经常用于表示文件名,但是也可用于产生一般的字符串。注意括号扩展并不试图匹配已有文件名(区别于 "echo *"),示例如下:
需要注意的是,括号中必须要有逗号分割,不能有未转义的空白符(也就是前加 “\" 转义),否则 bash 不会进行符号扩展。$ ls $ echo chap_{one,two,three}.txt chap_one.txt chap_two.txt chap_three.txt
$ ls -F file1 file2 file3 $ mkdir vrs{A,B,C,D,E} $ ls -F file1 file2 file3 vrsA/ vrsB/ vrsC/ vrsD/ vrsE/
- 2) Tidle expansion
-
title 也就是 "~" 字符,当它出现在一个 token 的开始处时,会进行特殊对待。如果仅仅有一个 "~",bash 会把它扩展为 root 的home目录;如果"~" 后面跟一个有效用户名,扩展为此用户的 home 目录;否则,不是个有效用户的话就不会进行 Tidle expansion。
$ echo ~zach /home/zach $ echo ~root /root $ echo ~xx ~xx
另外 tidle 也用于 directory stack manipulation。"+" 和 "-" 分别对应于环境变量中的 PWD(当前路径)和 OLDPWD(上次路径)$ echo ~+ /home/commi/test $ echo ~- /home/commi
- 3) Parameter and variable expansion
-
"$" 后面没有跟一个 "(" 会导致进行 Parameter or variable expansion。当然如果 "$"前有 "\" 转义的话又是一说了。
- 4) Arithmetic expansion
-
形式为 "$((expression))" 的字符串,bash 会对expression 进行算术计算,并用求得的值来替换整个字符串(相当于 let)。在括号中若引用了变量,可以不加前导符 "$"
$ a=$((5+3)) $ let b=5+3 $ echo $a $b 8 8
- 5) Command ubstitution
-
对形似 "$(command)" 或 " `command` "(注意为反引号,在键盘感叹号!的左边)的字符串,用command执行的结果来替换原字符串
$ echo `uname -r` 3.9.5-301.fc19.x86_64 $ echo $(pwd) /home/commi
- 6) Word splitting
-
使用环境变量 IFS 来分割word。
- 7) Pathname expansion
-
又叫 filename generation 或者 globbing,对特殊字符 *、?、[ 和 ]进行处理,试着用对应目录下存在文件的文件名来进行补全或匹配,如果匹配失败,不会进行扩展。
$ ls test1 test2 test3 $ echo * test1 test2 test3 $ echo test? test1 test2 test3 $ echo test[1-3] test1 test2 test3 $ echo l* l*
- 8) Process substitution
-
也就是输入输出重定向。
备注
【1】 用双引号包围参数会导致 shell 仅对参数执行Parameter and variable expansion,而不会进行第三步中的其他替换或扩展。用单引号包围则会忽略第三步中的所有替换/扩展。
$ echo tmp* $max
tmp1 tmp2 tmp3 sonar
$ echo "tmp* $max"
tmp * sonar
$ echo 'tmp* $max'
tmp * $max
【2】 google上找到一篇 shell Introduction,里面有介绍 shell operates,参见
Shell Command Language (里面有介绍第2步解析token的——
Token Recognition)