:)

Bash 口袋参考指南

这本口袋参考指南讨论 GNU/Linux 和 Mac OS X 系统的主要 shell——Bash,尤其是 4.4 版本。Bash 也可用于 Solaris 及各种 BSD 系统,并且能够在任何其它的 Unix 系统上轻易编译,甚至包括 OpenVMS 系统。本书覆盖下列主题:

  • 历史
  • 功能概述
  • 执行 shell
  • 命令退出状态
  • 语法
  • 函数
  • 变量
  • 算术表达式
  • 命令历史
  • 可编程补全
  • 作业控制
  • Shell 选项
  • 命令执行
  • 协同进程
  • 限制 shell
  • 内置命令
  • 资源

约定

文件名、命令名、选项以及内联示例使用等宽字体显示。用户应该精确输入的内容显示为等宽粗体。在示例和语法描述中应被真实的数据所替换的文本使用等宽斜体显示。新的术语和强调词汇使用斜体显示。最后,name(N)形式参考引用联机手册 N 节中的 name 手册页(通过 man 命令访问)。Shell 变量值(包含环境变量)表示为 $VAR

历史

最初随 1979 年 V7 Unix 分发的 Bourne shell 变成了编写 shell 脚本的标准 shell。在许多商用 Unix 系统中仍然能找到 Bourne shell /bin/sh。自从初始发布以来,Bourne shell 并没有太多改变,尽管这些年它已经有了最现代的增强。添加的最显著的新特性包括 System III(大约 1980 年)具有的 CDPATH变量和内置 test 命令、针对 System V Release 2(大约 1984 年)的命令哈希和 shell 函数、以及额外针对 System V Release 4(1989 年)的作业控制功能。

因为伯克利 C shell(csh)提供诸如命令历史和作业控制这样更愉悦的交互式使用功能,所以在 Unix 世界的长期标准实践是编程使用 Bourne shell,而日常使用 C shell。贝尔实验室的 David Korn 是通过添加历史、作业控制、以及额外的可编程能力等 csh 类功能来增强 Bourne shell 的首位开发者。最终,Korn shell 的功能集超越了 Bourne 和 C 两个 shell,而且保留了针对以前 shell 编程的兼容性。如今,POSIX 标准定义了标准 shell 语言和基于 System V Bourne shell 的行为,并包含来自 Korn shell 的选择功能子集。

自由软件基金会继续它的使命来出品完整的 Unix 类工作系统,从头开发了 Bourne shell 的克隆 Bash(Bourne-Again SHell)。随着时间的推移,Bash 虽然已经成为与 Korn shell 具有功能重叠的 POSIX 兼容 shell 版本,但是 Bash 不是精确的 Korn shell 克隆。今天,Bash 也许是最广泛使用的 Bourne 衍生 shell。

功能概述

Bash shell 提供下列功能:

  • 输入/输出重定向
  • 用于文件名缩写的通配符
  • 用于定制环境的 shell 变量和选项
  • 用于编写 shell 程序的内置命令
  • 用于在 shell 程序中模块化任务的 shell 函数
  • 作业控制
  • 命令行编辑(要么使用 vi,要么使用 Emacs 的命令语法)
  • 访问以前的命令(命令历史)并能够编辑它们
  • 整数运算
  • 数组和算术表达式
  • 命令名称缩写(别名)
  • 与 POSIX 向上兼容
  • 国际化能力
  • for 循环运算

执行 shell

Bash shell(bash)的命令解释器可按以下方式执行:

bash [options] [arguments]

Bash 能够从终端、文件(当第一个参数是脚本时)或标准输入(如果没有参数剩余,或者指定 -s)执行命令。如果标准输入是终端或在命令行指定 -i,shell 自动打印提示符。

在很多系统上,/bin/sh 是 Bash 的链接。当作为 sh 执行时,Bash 实际上更像传统的 Bourne shell:登录 shell 读取 /etc/profile~/.profile,而一般 shell 读取 $ENV,如果它被设置的话。完整的细节可参考 bash(1) 手册页。

命令行选项

几乎所有的单字母命令行选项都可用于内置 set 命令(参见 set 节)。这些选项包括:

-c str

从字符串 str 读取命令。

-D, --dump-strings

打印程序中的所有 $"..." 字符串。

-i

创建交互式 shell(提示输入)。不能用于 set

-l, --login

行为像登录 shell。

-O option

启用 shopt 选项 option。使用 +O 来取消设置 option

-p

作为特权用户启动。既不会读取 $ENV$BASH_ENV,也不会从环境导入函数,而且忽略 BASHOPTSCDPATHGLOBIGNORESHELLOPTS 变量的值。一般读取固定名称的启动文件(如 ~/.bash_profile)。

-r, --restricted

创建限制 shell(参考“限制 Shell”一节)。

-s

从标准输入读取命令。来自内置命令的输出转到文件描述符 1,所有其它的 shell 输出转到文件描述符 2。

-v, --verbose

打印行,正如 shell 读取它们一样。

--debugger

如果调试配置在启动时可用,则读取它,并为 shopt 开启 extdebug 选项。用于 Bash 调试器(参考 http://bashdb.sourceforge.net)。

--dump-po-strings

-D 相同,但使用 GNU gettext 格式输出。

--help

打印用法,并成功退出。

--init-file file, --rcfile file

使用 file 作为启动文件代替 ~/.bashrc 以用于交互 shell。

--noediting

即使在交互 shell 中也不使用 readline 库用于输入。

--noprofile

不读取 /etc/profile 或任何个人启动文件。

--norc

不读取 ~/.bashrc。当作为 sh 执行时自动开启。

--posix

开启 POSIX 模式。

--version

打印版本信息并退出。

-, --

结束选项处理。

参考 set 一节了解其余选项。

参数

参数被分配到位置参数 $1$2 等。如果第一个参数是脚本,则从它读取命令,而余下的参数分配到 $1$2 等。脚本的名称可用作 $0。脚本文件本身不必可执行,但它必须可读取。

命令退出状态

当任何命令退出时,它提供一个数字化的退出状态或返回值。外部命令(如 ls)提供该值给操作系统。内部命令(如 cd)直接提供该值给 shell。

当命令退出时,shell 自动接收返回值。按照约定,0 退出状态意为真或成功。任何其它状态意为假或失败。这就是 shell 为何在它的控制流程语句(如 ifwhileuntil)中使用命令的原因。

另外,shell 将上次执行命令的返回值存储于 $?,shell 脚本可以访问它。通常你应当使用另一个变量保存它,因为脚本中后面的命令将覆盖它。

退出值的范围从 0 到 255。shell 使用特定的数字值表示确定的条件:

数字值 含义
0 成功
2 由内置命令返回,表示用法错误
126 命令已找到,但不可执行
127 命令未找到
128 + N 由于收到信号数 N,命令停止执行
## 语法

本节描述 shell 的许多特殊符号,主题安排如下:

  • 特殊文件
  • 文件名元字符
  • 花括号展开
  • 转义序列
  • 引用
  • 命令形式
  • 重定向形式

特殊文件

shell 读取一或多个启动文件。有些文件仅当 shell 作为登录 shell 时才读取。Bash 按以下顺序读取文件:

  • /etc/profile。在登录时自动执行。
  • 从该列表中找到第一个文件:~/.bash_profile~/.bash_login、或 ~/.profile。在登录时自动执行。
  • ~/.bashrc 由每个非登录 shell 读取。然而,如果作为 sh 执行或使用 --posix,Bash 为了 POSIX 兼容性代替读取 $ENV

getpwnam()getpwuid() C 库函数是 ~name 缩写的主目录来源。(在个人系统上,用户数据库存储在 /etc/passwd 中。然而,在网络系统上,该信息可以来自 NIS、NIS+、LDAP 或某些其它来源,并非工作站密码文件。)

当交互登录 shell 退出时,或当非交互登录 shell 执行 exit 内置命令时,如果 ~/.bash_logout 文件存在,那么 Bash 将读取并执行它。(登录 shell 是设置了 -l 选项的一种。)

文件名元字符

元字符 含义
* 匹配零个或多个任意字符
? 匹配任意单个字符
[abc...] 匹配中括号中的任意一个字符,- 可以指定范围(如 a-z、A-Z、0-9)
[!abc...] 匹配不在中括号中的任意字符
~ 当前用户的主目录
~name name 用户的主目录
~+ 当前工作目录($PWD
~- 以前的工作目录($OLDPWD

使用 extglob 选项时:

模式 含义
?(pattern) 匹配 pattern 的零个或一个实例
*(pattern) 匹配 pattern 的零个或多个实例
+(pattern) 匹配 pattern 的一个或多个实例
@(pattern) 匹配 pattern 的一个实例
!(pattern) 匹配那些不匹配 pattern 的任意字符串

pattern 可以是由 | 分隔的模式序列,意为匹配应用于任意模式。这种扩展的语法类似于 egrepawk

使用 globstar 选项时:

字符 含义
** 匹配所有文件及零个或多个子目录。当跟着斜杠时,只有目录和子目录被匹配。

Bash 支持 POSIX [[=c=]] 表示匹配具有相同重音的字符,而 [[.c.]] 指定依序序列。另外,字符集形式 [[:class:]] 允许你匹配下列字符集:

字符集 匹配的字符
alnum 字母和数字
alpha 字母
ascii ASCII 字符(不在 POSIX 中)
blank 空格或制表符
cntrl 控制字符
digit 十进制数字
graph 非空格字符
lower 小写字母
print 可打印字符
punct 标点符号
space 空白字符
upper 大写字母
word [[:word:]][[:alnum:]] 相同(不在 POSIX 中)
xdigit 十六进制数字

提示

Bash 一次读一行脚本。在开始执行行上的任意命令以前,它完全地解析每一行。这有两层含意:

  • 你不能定义别名,并在同行上使用它。
  • 你应当在脚本受影响的部分以前放置那些会影响脚本解析的命令。

相似的担心也适用于函数。它们一次被全部解析,因此你不能在函数主体中打开 extglob 选项来影响该函数。所以,为了使用扩展的模式匹配能力,你应当将此命令放在脚本的开头:

shopt -s extglob    # 开启扩展的 shell 模式

示例

命令 含义
ls new* 列出 new 和 new.1
cat ch? 匹配 ch9,但不匹配 ch10
gvim [D-R]* 匹配 D 到 R 开头的文件
pr !(*.o|core) | lpr 打印非对象及非核心文件

注意

在现代系统上,诸如 [D-R] 的范围不可移植。系统的区域可能包含超出范围中从 D 到 R 的大写字母。不过,参考 globasciiranges shell 选项了解控制的方法。

花括号展开

基于来自 C shell 的类似功能,Bash 已长期支持花括号展开。不像文件名元字符,花括号展开是纯粹的文本化,由花括号展开创建的单词不会匹配现有的文件。这有两种形式:

pre{X,Y[,Z...]}post

展开为 preXpostpreYpost 等。

pre{start..end[..incr]}post

startend 既可以是整数,也可以是单个字母。incr 是一个整数。shell 展开这种结构为 startend 之间的完整范围,如果提供 incr,则通过 incr 递增。

前缀和后缀文本在任一种形式都不是必须的。对于数字化展开,startend 或两个都可以使用一个或多个前导零作为前缀。这种展开的结果使用零来填充 startend 的最大宽度。Bash 忽略 incr 上的前导零,总是将它当作十进制值对待。

花括号展开可以嵌套,而且结果不会排序。花括号展开在其它展开以前执行,不必为了让 Bash 识别它们而引起开和闭花括号。Bash 跳过花括号展开中的命令替换。为了避免与参数展开的冲突,${ 不能开始一个花括号展开。

示例

# 展开文本,不排序
$ echo hi{DDD,BBB,CCC,AAA}there
hiDDDthere hiBBBthere hiCCCthere hiAAAthere

# 展开,然后匹配 ch1、ch2、app1、app2
$ ls {ch,app}?

# 展开为 mv info info.old
$ mv info{,.old}

# 简单的数字化展开
$ echo 1 to 10 is {1..10}
1 to 10 is 1 2 3 4 5 6 7 8 9 10

# 含有递增的数字化展开
$ echo 1 to 10 by 2 is {1..10..2}
1 to 10 by 2 is 1 3 5 7 9

# 含有零填充的数字化展开
$ echo 1 to 10 with zeros is {01..10}
1 to 10 with zeros is 01 02 03 04 05 06 07 08 09 10

转义序列

Bash 在三种不同的上下文中理解并解释特殊的转义序列:

  • $'...' 引起字符串
  • echo -eprintf %b 的参数
  • 用于 printf 的格式化字符串

下表列出常用的转义序列(在全部上下文中都接受)以及上述每种上下文所用的唯一转义序列:

序列 可用性
\a 全部 ASCII BEL(可视可声音告警)
\b 全部 退格
\c echo -eprintf %b 抑制终端换行(像 echo -n),且不打印任意跟着的字符
\cX $'...' 控制字符 X
\e 全部 转义
\E 全部 转义
\f 全部 换页
\n 全部 换行
\r 全部 硬回车
\t 全部 制表符
\uHHHH 全部 Unicode 字符 HHHH
\UHHHHHHHH 全部 Unicode 字符 HHHHHHHH
\v 全部 垂直制表符
\xHH 全部 十六进制值 HH
\nnn $'...'printf 八进制值 nnn
\0nnn echo -eprintf %b 八进制值 nnn
\' $'...' 单引号
\" $'...' 双引号
\? $'...' 问号
\\ 全部 反斜杠

另外,shell 解释 PS0PS1PS2PS4 提示字符串值中少量重叠的转义序列。该内容在“特殊的提示字符串”一节中讨论。

引用

引用禁用一个字符的特殊含义,并允许它作为字面量使用。下表显示具有特殊含义的字符:

字符 含义
; 命令分隔符
& 后台执行
() 命令分组
| 管道
< > & 重定向符号
* ? [ ] ~ + - @ ! 文件名元字符
" ' \ 用于引用其它字符
` 命令替换
$ 变量替换(或命令或算术替换)
# 开始注释直到行尾
space tab newline 单词分隔符