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
,也不会从环境导入函数,而且忽略 BASHOPTS
、CDPATH
、GLOBIGNORE
及 SHELLOPTS
变量的值。一般读取固定名称的启动文件(如 ~/.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
为何在它的控制流程语句(如 if
、while
及 until
)中使用命令的原因。
另外,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
可以是由 |
分隔的模式序列,意为匹配应用于任意模式。这种扩展的语法类似于 egrep
和 awk
。
使用 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
展开为 preXpost
、preYpost
等。
pre{start..end[..incr]}post
start
和 end
既可以是整数,也可以是单个字母。incr
是一个整数。shell
展开这种结构为 start
和 end
之间的完整范围,如果提供 incr
,则通过 incr
递增。
前缀和后缀文本在任一种形式都不是必须的。对于数字化展开,start
或 end
或两个都可以使用一个或多个前导零作为前缀。这种展开的结果使用零来填充 start
和
end
的最大宽度。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 -e
和printf %b
的参数 - 用于
printf
的格式化字符串
下表列出常用的转义序列(在全部上下文中都接受)以及上述每种上下文所用的唯一转义序列:
序列 | 可用性 | 值 |
---|---|---|
\a |
全部 | ASCII BEL(可视可声音告警) |
\b |
全部 | 退格 |
\c |
echo -e 、printf %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 -e 、printf %b |
八进制值 nnn |
\' |
$'...' |
单引号 |
\" |
$'...' |
双引号 |
\? |
$'...' |
问号 |
\\ |
全部 | 反斜杠 |
另外,shell 解释 PS0
、PS1
、PS2
、PS4
提示字符串值中少量重叠的转义序列。该内容在“特殊的提示字符串”一节中讨论。
引用
引用禁用一个字符的特殊含义,并允许它作为字面量使用。下表显示具有特殊含义的字符:
字符 | 含义 |
---|---|
; |
命令分隔符 |
& |
后台执行 |
() |
命令分组 |
| |
管道 |
< > & |
重定向符号 |
* ? [ ] ~ + - @ ! |
文件名元字符 |
" ' \ |
用于引用其它字符 |
` | 命令替换 |
$ |
变量替换(或命令或算术替换) |
# |
开始注释直到行尾 |
space tab newline |
单词分隔符 |