当前位置: 首页 > 文档资料 > BASH 中文文档 >

第三章 Shell 的基本功能

优质
小牛编辑
134浏览
2023-12-01

Bash 是 Bourne-Again SHell 的缩略词,而 Bourne (波恩)Shell 是由史蒂夫·伯恩所做的传统 Unix Shell,所有波恩 Shell 内部命令在 Bash 中同样可用,而求值和引用的规则却是来自 POSIX 规范中定义的标准 Unix Shell。

本章简要介绍了 Bash 的结构:命令、控制结构、Shell 函数、Shell 变量、Shell 扩展、重定向 -- 即把输入和输出定向到 (自) 文件,以及 Shell 是怎么执行命令的。

3.1 Shell 语法

Shell 在读取输入时,要经过一系列的操作。如果在输入中开始了一个注释,Shell 会把注释符 (#)以及后面的一整行都忽略掉。否则概括的说,Shell 会读取输入并将之分解为一个个单词和运算符,并使用引用规则来决定每个单词和字符的不同含义。然后 Shell 会把这些解析为命令和其它结构,去除一些特定单词的特殊含义,对另外一些进行扩展,根据需要进行重定向,执行指定的命令,等待其退出状态,并让这个状态能用于后续检查或处理。

3.1.1 Shell 操作

下面简要说明了 Shell 读取和执行命令时所进行的操作。简单的说,Shell 执行了下面的操作:

  1. 从文件 (参见 3.8 Shell 脚本),或启动“-c”选项的字符串参数 (参见 6.1 Bash 的启动) 中,或者用户的终端上读取输入。
  2. 按照 3.1.1.1 引用 中所述规则把输入分解为单词和运算符。这些符号用元字符分隔。该步骤还进行别名扩展 (参见 6.6 别名)。
  3. 把符号解析为简单和复杂命令 (参见 3.2 Shell 命令)。
  4. 进行各种 Shell 扩展 (参见 3.5 Shell 扩展),并把扩展后的符号分解为文件名 (参见 3.5.8文件名扩展名)。
  5. 进行必要的重定向 (参见 3.6 重定向),并把重定向运算符及其参数从参数列表中去掉。
  6. 执行得到的命令 (参见 3.7 命令的执行)。
  7. (可选的) 等待命令结束并收集其退出状态 (3.7.5 退出状态)。

3.1.1.1 引用

引用在 Shell 中用以去除某些字符或单词的特殊含义。它可以用来禁止对特殊字符的特殊处理,使得保留字不再被认为是保留字,或者禁止参数扩展。

Shell 的每个元字符在 Shell 中都有特殊的含义,必须引用后才能代表其自身。如果使用了命令历史扩展的功能 (9.3 历史扩展),则历史扩展字符 (通常是 ! ) 也引用起来以取消历史扩展。有关历史扩展的更多细节,请参见 9.1 Bash 的历史功能。

Bash 中有三种引用机制:转义字符,单引用和双引用。

3.1.1.2 转义字符

在 Bash 中,没有转义的反斜杠 \ 是转义字符,它能保留其下一个字符的字面含义,除非这个字符是换行符。如果出现 newline(即 \ 是某行的最后一个字符。)这样的序列,并且反斜杠本身没有被引用,则 newline 就是行连续符,也就是说,它们将会从输入流中被删除并被完全忽略掉。

3.1.1.3 单引用

把字符串用单引号 ('天 引用能保留引号内各个字符的字面含义。在单引号中不允许再出现单引号,即使它已经由反斜杠转义。

3.1.1.4 双引用

把字符串用双引号 (")引用能保留引号内各个字符的字面含义,除非这些字符是 $、'、 、以及 ! (如果已经打开历史扩展)。在双引号中,$ 和 ' 继续保留其特殊的功能 (参见 3.5 Shell 扩展)。而反斜杠,只有当其后面的字符是 $、'、"、 或者换行符时才保留其特殊的含义。

在双引号中,如果反斜杠后面是这些字符中的任意一个,则这个反斜杠就会被删除。而它后面字符如果没有特殊的含义,则它将被保留。在双引号中可以出现另外一个双引号,只要它在反斜杠后面。如果打开了历史扩展,! 将导致历史扩展,除非它由反斜杠转义。在 ! 前的反斜杠不会被删除。

特殊变量 * 和 @ 在双引号中有特殊的含义;请参见 3.5.3 Shell 参数扩展。

3.1.2 ANSI 标准 C 引用

形如 $'string' 的单词会被特殊处理。这个词将会扩展成一个字符串,其中的转义字符会按照 ANSI C 标准被替换。如果其中出现转义字符序列,则按照下面的规则解释:

\a 警告(响铃)
\b 退格删除
\e 转义字符 (不属于 ANSI C)
\f 走纸换
\n 新行
\r 换行
t(水平)制表符
\v 垂直制表符
\\ 反斜杠
\' 单引号
\nn 由八进制数 nnn (一个到三个数字天 代表的一个八位字符。
\xHH 由十六进制数 HH (一个到两个数字天 代表的一个八位字符。
\cx 一个控制字符

扩展的结果是一个单引用,就好像美元符号原本就不存在一样。

不能用双引号转义

在很多命令中都需要指定单个字符,例如 tr 或 awk 中的 IFS 等变量。这时就应该使用 ANSI 标准 C 引用,而不能用双引号转义。例如

tr 

可以把文件 file 的所有行用空格连在一起。

3.1.2.1 Locale 专用的翻译‌

双引用的字符串在美元符号($)后面将使得该字符串根据当前的 locale 被翻译过来。如果当前 locale 是 C 或者 POSIX,则美元符号将被忽略。如果字符串被翻译或者替换,则替换后的字符串是双引用的。

有些系统使用 LC MESSAGES 这个 Shell 变量来选择消息目录。也有些系统根据 TEXTDOMAIN 这个 Shell 变量来决定消息目录的名称,有可能还要加上 .mo 后缀。如果使用了 TEXTDOMAIN 变量,可能还需要把 TEXTDOMAINDIR 设为存放消息目录文件的路径。更有一些系统这样使用这两个变量:

TEXDOMAINDIR/LC MESSAGES/LC MESSAGES/TEXTDOMAIN.mo

3.1.3 注释

在非交互运行的 Shell 中,或者交互运行的 Shell 如果打开了内部命令 shopt 的 interactive comments 选项 (参见 8.7 内部命令 shopt),以 # 开头的单词将使得该单词及本行中所有其它单词都被忽略。如果交互运行的 Shell 没有打开 interactive comments 选项则不允许注释。在默认情况下,交互运行的 Shell 已经打开了 interactive comments 选项。至于如何让 Shell 变成交互式的,请参见 6.3 交互式 Shell。

3.2 Shell 命令

一个简单的 shell 命令,例如 echo abc,包含该命令本身,其后还有一些参数;它们都用空格分隔。

复杂一些的命令由有简单的命令通过各种方式组合而成的:通过管道命令,这时一个命令的输出成为另一个命令的输入;或者通过循环或条件命令;或者通过其它组合方式。

3.2.1 简单命令

简单命令使用得最频繁。它仅仅包括空白符分隔的多个单词,其结尾是一个 shell 控制运算符。其中的第一个单词通过指定要执行的命令,而后续单词都是这个命令的参数。

简单命令的返回状态(参见 3.7.5 退出状态)是 POSIX 1003.1 中的 waitpid 函数规定的退出状态;如果该命令由一个信号 n 终止,则其退出状态是 128+n。

3.2.2 管道

管道是由控制字符 | 或 |& 分隔开的一系列简单命令,管道的其格式为:

[time [-p]] [!] 命令1 [[|或&] 命令2 …]

管道里面每个命令的输出都经由管道与下一个命令的输入相连接;也就是说,每个命令都去读取上一个命令的输出。这种连接早在命令中指定的任何重定向之前就已经进行了。

如果使用了|&,则命令一的标准错误输出将会和命令二的标准输出相连,这是 2>&1 | 的简写形式。这种对标准错误输出的隐含重定向是在命令中指定的任何重定向之后进行的。

保留字 time 能够在管道执行完毕后输出其执行时间的统计信息。这个统计目前包括执行该命令所花费的总时间夨钟表时间天以及用户和系统时间。-p 选项助记词: POSIX使得输出的形式和奐奏奓奉奘中的规定相同。可以设置TIMEFORMAT 变量为一个格式化字符串,以指定时间输出的形式。至于所有可用的格式,请参见 5.2 Bash 的变量。把 time 作为保留字允许我们统计内部命令,shell 函数,以及管道的执行时间;

而如果 time 是外部命令就不能很容易的做到这一点。

time 和 times

在 Bash 中,time 是用于管道的保留字,而 times 是一个内部命令。它们的作用相同,使用的场合却不一样。参见 4.1 波恩 Shell 的内部命令 times。

如果该管道不是异步(参见 3.2.3 命令队列)执行的,则 shell 会等待管道中所有命令执行的结束。

管道里面的每个命令都是在自己的子 shell (参见 3.7.3 命令执行的环境)里面执行的。管道的退出状态是其中的最后一个命令的退出状态,除非打开了pipefail 选项(参见 86 内部命令)。如
果打开了pipefail 选项,则管道的返回状态是其中最后一个(最靠右的)返回非零的那个命令的状态;如果所有命令都成功执行,则返回零。如果管道的前端有保留字!,则其返回状态是按照如上据说再进行逻辑取反。shell 等待管道里面的所有命令的结束,然后才返回一个值。

3.2.3 命令队列

命令队列是由一个或者管道通过运算符;、&、&&、|| 连接而成,最后还可以(可选的)由;、&、或换行符结束。

在之些队列运算符中,&& 和|| 具有同样的优先级;其次是; 和&,这两个也有同样的优先级。

在命令队列中可以使用一个或多个换行符分隔命令,这与分号是等价的。

如果一个命令是由控制字符& 结束,则 shell 会不同步的在 shell 中执行该命令。我们通常称之为在“后台”运行该命令。这时 shell 并不等待命令的结束,而其返回状态0 (即逻辑真)。如果没有启用作业控制(参见 7 作业控制),并且也没有显式指定重定向,则在异步执行的命令的标准输入将被重定向到 /dev/null。

\n'  '  ' file

可以把文件 file 的所有行用空格连在一起。

3.1.2.1 Locale 专用的翻译‌

双引用的字符串在美元符号($)后面将使得该字符串根据当前的 locale 被翻译过来。如果当前 locale 是 C 或者 POSIX,则美元符号将被忽略。如果字符串被翻译或者替换,则替换后的字符串是双引用的。

有些系统使用 LC MESSAGES 这个 Shell 变量来选择消息目录。也有些系统根据 TEXTDOMAIN 这个 Shell 变量来决定消息目录的名称,有可能还要加上 .mo 后缀。如果使用了 TEXTDOMAIN 变量,可能还需要把 TEXTDOMAINDIR 设为存放消息目录文件的路径。更有一些系统这样使用这两个变量:

3.1.3 注释

在非交互运行的 Shell 中,或者交互运行的 Shell 如果打开了内部命令 shopt 的 interactive comments 选项 (参见 8.7 内部命令 shopt),以 # 开头的单词将使得该单词及本行中所有其它单词都被忽略。如果交互运行的 Shell 没有打开 interactive comments 选项则不允许注释。在默认情况下,交互运行的 Shell 已经打开了 interactive comments 选项。至于如何让 Shell 变成交互式的,请参见 6.3 交互式 Shell。

3.2 Shell 命令

一个简单的 shell 命令,例如 echo abc,包含该命令本身,其后还有一些参数;它们都用空格分隔。

复杂一些的命令由有简单的命令通过各种方式组合而成的:通过管道命令,这时一个命令的输出成为另一个命令的输入;或者通过循环或条件命令;或者通过其它组合方式。

3.2.1 简单命令

简单命令使用得最频繁。它仅仅包括空白符分隔的多个单词,其结尾是一个 shell 控制运算符。其中的第一个单词通过指定要执行的命令,而后续单词都是这个命令的参数。

简单命令的返回状态(参见 3.7.5 退出状态)是 POSIX 1003.1 中的 waitpid 函数规定的退出状态;如果该命令由一个信号 n 终止,则其退出状态是 128+n。

3.2.2 管道

管道是由控制字符 | 或 |& 分隔开的一系列简单命令,管道的其格式为:

管道里面每个命令的输出都经由管道与下一个命令的输入相连接;也就是说,每个命令都去读取上一个命令的输出。这种连接早在命令中指定的任何重定向之前就已经进行了。

如果使用了|&,则命令一的标准错误输出将会和命令二的标准输出相连,这是 2>&1 | 的简写形式。这种对标准错误输出的隐含重定向是在命令中指定的任何重定向之后进行的。

保留字 time 能够在管道执行完毕后输出其执行时间的统计信息。这个统计目前包括执行该命令所花费的总时间夨钟表时间天以及用户和系统时间。-p 选项助记词: POSIX使得输出的形式和奐奏奓奉奘中的规定相同。可以设置TIMEFORMAT 变量为一个格式化字符串,以指定时间输出的形式。至于所有可用的格式,请参见 5.2 Bash 的变量。把 time 作为保留字允许我们统计内部命令,shell 函数,以及管道的执行时间;

而如果 time 是外部命令就不能很容易的做到这一点。

time 和 times

在 Bash 中,time 是用于管道的保留字,而 times 是一个内部命令。它们的作用相同,使用的场合却不一样。参见 4.1 波恩 Shell 的内部命令 times。

如果该管道不是异步(参见 3.2.3 命令队列)执行的,则 shell 会等待管道中所有命令执行的结束。

管道里面的每个命令都是在自己的子 shell (参见 3.7.3 命令执行的环境)里面执行的。管道的退出状态是其中的最后一个命令的退出状态,除非打开了pipefail 选项(参见 86 内部命令)。如
果打开了pipefail 选项,则管道的返回状态是其中最后一个(最靠右的)返回非零的那个命令的状态;如果所有命令都成功执行,则返回零。如果管道的前端有保留字!,则其返回状态是按照如上据说再进行逻辑取反。shell 等待管道里面的所有命令的结束,然后才返回一个值。

3.2.3 命令队列

命令队列是由一个或者管道通过运算符;、&、&&、|| 连接而成,最后还可以(可选的)由;、&、或换行符结束。

在之些队列运算符中,&& 和|| 具有同样的优先级;其次是; 和&,这两个也有同样的优先级。

在命令队列中可以使用一个或多个换行符分隔命令,这与分号是等价的。

如果一个命令是由控制字符& 结束,则 shell 会不同步的在 shell 中执行该命令。我们通常称之为在“后台”运行该命令。这时 shell 并不等待命令的结束,而其返回状态0 (即逻辑真)。如果没有启用作业控制(参见 7 作业控制),并且也没有显式指定重定向,则在异步执行的命令的标准输入将被重定向到 /dev/null。