3.1 Shell 语法(Shell Syntax)
当 shell 读取输入时,它会进行一系列的操作。如果这个输入是由一个注释符“#”开始,shell 会忽略这个符号以及后面的所有内容。
如果输入的不是注释,粗略的讲,shell 会读取这行输入并将其分为单词和操作符,利用引用规则(quoting rules)给单词和字符选择出不同的指派意义。
之后 shell 将这些由单词和操作符组成的标记进行分析,得到命令或是其它结构。之后移除一些具有特殊含义的单词或字符,再将剩余的进行扩展,重定向输入或输出(如果需要的话),执行指定的命令,等待命令完成后的退出状态,并且使退出状态可用于下一步的检查或处理。
3.1.1 Shell 操作(Shell Operation)
当 shell 读取和执行一个命令时,下面是这个 shell 操作的简要描述步骤。基本上,shell 会进行下面的动作:
- 从文件(参见 3.8《Shell 脚本》)中读取,或从 Bash 的“-c”选项(参见 6.1《调用 Bash》)提供的参数中读取,或是从用户终端输入的字符串作为输入。
- 将输入的字符串按引用规则(参见 3.1.2《引用》)分割为单词和操作符做为标记。这些标记是由元字符分隔的。别名扩展(参见 6.6《别名》)是在这步执行的。
- 将这些标记解析为简单命令或是复合命令(参见 3.2《Shell 命令》)。
- 执行各类 shell 扩展(参见 3.5《Shell 扩展》),将扩展后的标记分割为文件名列表(参见 3.5.8《文件名扩展》)和命令与参数(arguments)。
- 按需执行重定向(参见 3.6《重定向》)并从参数列表中移除重定向操作符和操作数。
- 执行命令(参见 3.7《命令执行》)。
- 作为可选步骤,等待命令完成并收集该命令的退出状态(参见 3.7.5《退出状态》)。
3.1.2 引用(Quoting)
引用是 shell 用于移除一些字符或单词的特殊含义。引用会取消对一些特殊字符的特别处理,例如防止识别保留字,和防止参数扩展。
shell 的每一个元字符(参见第 2 章《名词定义》)对于 shell 来说都具有特殊意义,如果要表示其字符本身,就必须将其引起来。当开启了命令历史扩展功能(参见 9.3《历史扩展》)时,历史扩展字符,通常是叹号“!”,必须被引起来才能防止进行历史扩展。可参见 9.1《Bash 历史功能》对历史扩展的详细描述。
这里有三种引用机制:转义字符(escape character)、单引号引用(single quotes)和双引号引用(double quotes)。
3.1.2.1 转义字符(escape character)
一个未被引起来的反斜线“\”是 Bash 的转义字符。它用来保留其后面跟随字符的本身字母值,但除了后面是“newline”。如果“\newline”出现,并且反斜线没有被引起来,这个“\newline”将被视为行继续符。(也就是说,它会从输入流中移除并且被有效的忽略)
3.1.2.2 单引号引用(single quotes)
将字符使用单引号“‘”引起来时,其中的字符会保留其字母值。单引号不能出现在单引号之间,即使使用转义字符对单引号进行转义。
3.1.2.3 双引号引用(double quotes)
用双引号““”引起来的字符也会被保留其字母值,但除了美圆符号“$”、反引号“`”、反斜线“\”和开启了历史扩展后的叹号“!”。当 shell 处于 POSIX 模式时,即使开启了历史扩展,在双引号内的叹号也不会有特殊含义存在。在双引号里的美圆符号“$”和反引号“`”保留其特殊含义。只有在反斜线后面跟随了“$”、“`”、“””、“\”或“newline”,反斜线才具有特殊含义。在双引号里,这些特殊符号前面的反斜线会被移除,也就是对这些特殊符号进行了转义使其不再具有特殊含义,反而表示这些符号的本身字母值。要在双引用里表示双引号的字母值,需要在其前面使用反斜线进行转义。如果开启了历史扩展,在双引号里会执行历史扩展,除非在叹号前面使用反斜线将其转义为符号的字母值。但在叹号之前的反斜线不会被移除。
特殊参数“*”和“@”在双引用里具有特殊含义(详细说明参见 3.5.3《Shell 参数扩展》)。
3.1.2.4 ANSI-C 引用(ANSI-C Quoting)
以“$‘string’”形式出现的单词会被特殊处理。该单词会被扩展为字符串,其里面使用反斜线转义的字符会使用 ANSI C 标准进行转义。反斜线转义的字符序列,如下所示:
| 转义序列 | 含义 |
|---|---|
| \a | 报警 即“铃声” |
| \b | 删除(backspace) |
| \e \E | 转义字符(非 ANSI C) |
| \f | 换页(Form feed) |
| \n | 换行(newline) |
| \r | 回车(carriage return) |
| \t | 水平制表(horizontal tab) |
| \v | 垂直制表(vertical tab) |
| \\ | 反斜线 |
| \‘ | 单引号 |
| \“ | 双引号 |
| \? | 问号 |
| \nnn | 使用 3 位(一到三位)八进制表示的 8 位字长的一个二进制字符 |
| \xHH | 使用 2 位(一到两位)十六进制表示的 8 位字长的一个二进制字符 |
| \uHHHH | 使用 4 位(一到四位)十六进制表示的一个 Unicode(ISO/IEC 10646)字符 |
| \uHHHHHHHH | 使用 8 位(一到八位)十六进制表示的一个 Unicode(ISO/IEC 10646)字符 |
| \cx | control-x 字符 |
除了以上的 ANSI-C 转义,其它扩展结果就像没有美圆符号的单引用一样。
3.1.2.5 特殊区域语言转换(Locale-Specific Translation)
美圆符号“$”后加双引号字符串会使字符串进行当前本地区域语言转换。“gettext”基础结构使用 shell 变量“LC_MESSAGES”和“TEXTDOMAIN”进行消息编目查询和转换。可参见“gettext”命令文档的详细说明。如果当前区域语言是 C 或 POSIX,或没有转换可用,则美圆符号是被忽略的。如果对字符串进行了转换,则这个转换是双引号引用转换。
一些系统使用由 shell 变量“LC_MESSAGES”选择的消息编目。其它的则是从 shell 变量“TEXTDOMAIN”中创建的消息编目名,也可能会添加后缀“.mo”。如果你使用变量“TEXTDOMAIN”,你可能需要设置变量“TEXTDOMAINDIR”来定位消息编目文件的位置。仍还有其它的系统以“TEXTDOMAINDIR/LC_MESSAGES/LC_MESSAGES/TEXTDOMAIN.mo”这样的风格使用这两个变量。
3.1.3 注释(Comments)
在非交互式 shell 中,或在使用了 shopt 开启了“interactive_comments”选项的交互式 shell 中,一个以井号“#”开头的单词和其这一行后面的所有字符将被忽略。
一个没有开启“interactive_comments”选项的交互式 shell 则不会允许使用注释。“interactive_comments”选项在交互式 shell 中默认是开启的。可参见第 6.3 章节《交互式 shell》关于是什么使 shell 具有交互性的介绍。