3.1 Shell 语法(Shell Syntax)

当 shell 读取输入时,它会进行一系列的操作。如果这个输入是由一个注释符“#”开始,shell 会忽略这个符号以及后面的所有内容。

如果输入的不是注释,粗略的讲,shell 会读取这行输入并将其分为单词和操作符,利用引用规则(quoting rules)给单词和字符选择出不同的指派意义。

之后 shell 将这些由单词和操作符组成的标记进行分析,得到命令或是其它结构。之后移除一些具有特殊含义的单词或字符,再将剩余的进行扩展,重定向输入或输出(如果需要的话),执行指定的命令,等待命令完成后的退出状态,并且使退出状态可用于下一步的检查或处理。

3.1.1 Shell 操作(Shell Operation)

当 shell 读取和执行一个命令时,下面是这个 shell 操作的简要描述步骤。基本上,shell 会进行下面的动作:

  1. 从文件(参见 3.8《Shell 脚本》)中读取,或从 Bash 的“-c”选项(参见 6.1《调用 Bash》)提供的参数中读取,或是从用户终端输入的字符串作为输入。
  2. 将输入的字符串按引用规则(参见 3.1.2《引用》)分割为单词和操作符做为标记。这些标记是由元字符分隔的。别名扩展(参见 6.6《别名》)是在这步执行的。
  3. 将这些标记解析为简单命令或是复合命令(参见 3.2《Shell 命令》)。
  4. 执行各类 shell 扩展(参见 3.5《Shell 扩展》),将扩展后的标记分割为文件名列表(参见 3.5.8《文件名扩展》)和命令与参数(arguments)。
  5. 按需执行重定向(参见 3.6《重定向》)并从参数列表中移除重定向操作符和操作数。
  6. 执行命令(参见 3.7《命令执行》)。
  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)字符
\cxcontrol-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 具有交互性的介绍。