封面

版权信息

O’Reilly Media, Inc. 介绍

业界评论

前言

目标读者

关于本书

排版约定

使用示例代码

O’Reilly在线学习平台(O’Reilly Online Learning)

联系我们

致谢

更多信息

第 1 章 bash入门

1.1 为什么是bash

NOTE

因为 bash 无处不在。它未必是最时髦的,可以说也不是最炫或最厉害的(就算不是,也差不多了),更不是唯一一个作为开源软件发布的 shell,但 bash 的大名尽人皆知

2022-03-20 00:26:46

NOTE

POSIX shell,bash 最初正是该项目的一部分

2022-03-20 00:27:38

NOTE

bash 既是一门强大的编程语言,也是一种优秀的用户界面。它让你在获得复杂编程特性的同时,能够保持键盘输入的便捷性。

2022-03-20 00:28:20

1.2 bash shell

NOTE

自动化,以实现易用性、可靠性以及可重现性

2022-03-20 17:49:54

1.3 提示符揭秘

1.4 显示当前位置

NOTE

pwd 是 print working directory(打印工作目录)的缩写,该命令接受两个选项。-L 显示当前的逻辑路径,这也是默认选项。-P 显示当前的物理路径,如果跟随符号链接,结果可能和逻辑路径不同。与此类似,cd 命令也提供了 -P 和 -L 选项:

2022-03-20 17:51:05

1.5 查找并运行命令

NOTE

可以试试 type、which、apropos、locate、slocate、find 和 ls 命令。

2022-03-20 17:51:35

1.6 获取文件的相关信息

1.7 显示当前目录下的所有隐藏(点号)文件

1.8 使用shell引用

1.9 使用或替换内建命令与外部命令

1.10 确定是否处于交互模式

1.11 将bash安装为默认shell

1.12 持续更新bash

1.13 获取Linux版的bash

1.14 获取xBSD版的bash

1.15 获取macOS版的bash

1.16 获取Unix版的bash

1.17 获取Windows版的bash

1.18 不获取bash的情况下使用bash

1.19 更多的bash文档

第 2 章 标准输出

NOTE

例如,再也不用让操作员将磁带挂载到磁带驱动器上(至少我们见过的桌面系统和笔记本计算机不用了)

2022-03-28 13:46:11

NOTE

软件开发人员是否要针对各种输出设备编写代码,甚至包括尚未发明的设备?这显然是件麻烦事。用户是不是也得知道如何将想要运行的程序连接到不同种类的设备?这也不是什么好主意

2022-03-28 13:59:38

NOTE

操作系统负责实现这套魔法。无论你要写入的目标是磁盘文件、终端、磁带设备、记忆棒,还是其他东西,程序只需要知道如何写入文件就够了,剩下的事情由操作系统搞定

2022-03-28 20:56:13

NOTE

程序怎么知道是该写入代表终端窗口的文件、磁盘文件还是其他种类的文件?不难,这种事情留给 shell 就行了

2022-03-28 20:56:29

NOTE

考虑下面这条简单的命令:[插图]

2022-03-28 20:56:58

2.1 输出到终端/终端窗口

NOTE

shell 负责解析 echo 的命令行参数(shell 对其他命令也是如此)。这意味着,在将参数交给 echo 前,shell 会完成所有的替换、通配符匹配等操作。其次,在解析参数时,参数之间的空白字符会被忽略

2022-03-20 17:54:02

2.2 保留输出中的空白字符

2.3 在输出中加入更多格式控制

2.4 消除输出中的换行符

NOTE

该特性在 shell 脚本中用处更大,你可能希望在形成一整行前由多条语句逐部分输出,或者在读取输入前显示用户提示

2022-03-28 21:41:02

NOTE

转移序列与 C 语言字符串中的类似。要想使用它们,调用 echo 命令时必须加上 -e 选项

2022-03-28 21:41:33

2.5 保存命令输出

NOTE

cat 命令得名自一个较长的单词 concatenation(拼接)

2022-03-28 21:42:52

2.6 将输出保存到其他文件

NOTE

如果文件名以斜线(/)起始,那就是绝对路径名

2022-03-28 21:43:14

NOTE

放置在文件系统层次结构(目录树)中以根目录起始的指定位置(假设所有的中间目录都存在且你有权限进入)

2022-03-28 21:43:26

NOTE

/tmp,这是一个几乎所有 Unix 系统都存在且普遍可用的临时目录

2022-03-28 21:43:31

NOTE

每次引用 .. 就会在文件系统目录树中向上(往根的方向,并非惯常意义中的沿着树“向上”)移动一级

2022-03-28 21:43:53

2.7 保存ls命令的输出

NOTE

我们加上 -C 选项

2022-03-28 21:48:27

NOTE

shell 的重定向功能意在对所有程序保持透明,因此程序无须编写特定代码来让自身的输出能够被重定向

2022-03-28 21:49:01

NOTE

我们可以在程序中加入代码,判断输出何时被发往终端(参见 manisatty)。然后,程序就能够针对不同情况进行处理,这也正是 ls 所做的

2022-03-28 21:49:23

NOTE

那么用户可能希望按列输出(-C 选项)

2022-03-28 21:49:34

NOTE

man isatty

2022-03-28 21:51:51

2.8 将输出和错误消息发送到不同文件

NOTE

1> 和 2> 中,数字表示文件 描述符

2022-03-28 22:34:58

NOTE

1 代表标准输出(STDOUT)

2022-03-28 22:35:04

NOTE

2 代表标准错误(STDERR)

2022-03-28 22:35:24

NOTE

如果不指定数字,则假定为 STDOUT

2022-03-28 22:34:48

2.9 将输出和错误消息发送到同一文件

NOTE

又或者老式且略烦琐(但可移植性更好)的写法:[插图]

2022-03-28 22:37:48

NOTE

both 是准备向 STDERR 和 STDOUT 生成输出的(假想)程序

2022-03-28 22:38:56

NOTE

&> 和 >& 只是将 STDOUT 和 STDERR 发送到相同地方(这正是我们想做的)的便捷写法

2022-03-28 22:39:03

NOTE

1 用作重定向的目标,但是 >& 将 1 解释为文件描述符。实际上,2>&1 是一个实体(其中不允许出现空格),表示标准错误(2)会被重定向(>)到随后的文件描述符(&)1

2022-03-28 22:40:46

NOTE

2>& 必须作为整体出现,不能夹杂空格;否则,2 就成了另一个参数,而 & 代表与其表面完全不同的含义(与在后台运行命令有关)

2022-03-28 22:40:54

NOTE

所有的重定向操作符都带有一个前导数字(如 2>),而 >(标准输出的文件描述符)的默认数字为 1

2022-03-28 22:41:04

NOTE

有时候,错误消息在文件中出现的时间可能早于在屏幕上出现的时间。这与标准错误的无缓冲性质有关,当写入文件而不是屏幕时,这种现象会更加明显

2022-03-28 22:41:37

2.10 追加输出

2.11 仅使用文件的起始或结尾部分

NOTE

使用 head 或 tail 命令。默认情况下,head 输出指定文件的前 10 行,tail 输出最后 10 行

2022-03-28 22:41:52

NOTE

tail 还有 -f 和 -F 选项,这两个选项能够跟踪文件末尾的写入

2022-03-28 22:42:01

NOTE

head、tail、

2022-03-28 22:43:00

NOTE

cut 和 uniq

2022-03-28 22:43:05

2.12 跳过文件标题

NOTE

tail 命令的选项 -n number(或者 -number)可以指定相对于文件末尾的行偏移。因此,tail -n 10 file 会显示 file 的最后 10 行,这也是不指定任何选项时的默认处理方式

2022-03-28 22:44:36

2.13 丢弃输出

NOTE

Unix 和 Linux系统都存在一个特殊设备,该设备并非真实的硬件,而仅仅是一个位桶(bit bucket),我们可以将不需要的数据都扔进去。它就是 /dev/null,非常适用于此类场景

2022-03-29 07:37:27

2.14 保存或分组多个命令的输出

NOTE

花括号({})将这些命令组合在一起,然后将重定向应用于分组中所有命令的输出

2022-03-29 08:54:14

NOTE

花括号实际上是保留字,因此两侧必须有空白字符。另外,闭合花括号之前的拖尾分号也是不能少的

2022-03-29 08:56:18

NOTE

你也可以用括号(())告诉 bash 在子 shell 中运行这些命令,然后重定向整个子shell 的输出

2022-03-29 08:56:34

NOTE

第一处是语法上的,第二处是语义上的。从语法上来看,花括号两侧需要有空白字符,命令列表中的最后一个命令必须以分号结尾。如果使用括号,那就不要求这些了

2022-03-29 08:56:53

NOTE

花括号只是一种组合多个命令的方式而已,更像是重定向的便捷写法,这样我们就不用单独重定向各个命令了。而出现在括号中的命令是在 shell 的另一个实例中运行,也就是当前 shell 的子 shell

2022-03-29 08:57:08

NOTE

子 shell 几乎复刻了当前 shell 的环境,包括 $PATH 在内的变量都是一模一样的,但对陷阱的处理有所不同

2022-03-29 08:57:18

NOTE

陷阱的更多信息

2022-03-29 08:57:25

NOTE

因为 cd 命令是在子 shell 中执行的,所以退出子 shell 后,父 shell 的当前目录仍保持原样,shell 变量也不会发生变化

2022-03-29 08:57:36

NOTE

花括号有一种值得注意的用法,即能够形成更简洁的分支语句块

2022-03-29 09:04:28

NOTE

第一种形式,这种写法更清晰,适合更大范围的受众

2022-03-29 09:05:07

2.15 将输出作为输入,连接两个程序

NOTE

利用管道符号(|)将输出直接发送到下一个程序

2022-03-29 16:23:40

NOTE

使用管道符号意味着不用再创建临时文件,事后再将其删除

2022-03-29 16:24:06

NOTE

像 sort 这种程序,既能从标准输入中获取输入(通过 < 符号进行重定向),也能从作为参数的文件中获取输入。因此,可以按以下方式操作

2022-03-29 16:24:17

NOTE

不用再将输入重定向到 sort

2022-03-29 16:24:21

NOTE

这种程序称为过滤器,如果照此方式编写程序和 shell 脚本,则更有助于你个人以及你的同事

2022-03-29 16:30:55

NOTE

初级的并行处理机制

2022-03-29 16:31:02

NOTE

你可以让两个命令(程序)并行运行并共享数据:一个的输出作为另一个的输入。二者不必按顺序运行(一个结束,另一个接着开始),只要第一个命令产生可用数据,第二个命令立刻就可以开始处理

2022-03-29 16:31:22

NOTE

但要注意,按照这种方式运行的多个命令(通过管道相连)分别在多个独立的进程中运行。尽管这一细微之处经常被忽视,有时所带来的影响却不容小视

2022-03-29 16:31:38

NOTE

19.8 节会对此展开讨论

2022-03-29 16:31:42

NOTE

通过管道将大量数据传给 less 等命令时,总会出现这种事:一旦发现了要找的东西,就会想要退出,哪怕是管道中还有更多数据

2022-03-29 16:33:02

2.16 将输出作为输入,同时保留其副本

NOTE

2.16.2 解决方案

2022-03-29 16:37:27

NOTE

tee 命令

2022-03-29 17:30:06

NOTE

同时发送到 /tmp/x.x 和 awk

2022-03-29 17:31:58

NOTE

后者通过管道与 tee 的输出连接在一起

2022-03-29 17:32:06

NOTE

演示 tee 命令在命令序列中的用法

2022-03-29 17:32:16

NOTE

tee 命令可以用来代替重定向标准输出的做法

2022-03-29 17:34:23

NOTE

tee 的输出并未重定向到别处,因此会显示在屏幕上。但是输出的副本也会发送给指定的文件(如 cat /tmp/all.my.sources),以备后用

2022-03-29 17:34:43

NOTE

这意味着错误(如来自 find 命令)会显示在屏幕上,但不会出现在 tee 指定的文件中。我们可以在 find 命令中加入 2>&1

2022-03-29 17:35:07

2.17 以输出为参数连接两个程序

NOTE

将待删除的文件指定为命令参数

2022-03-29 17:36:02

NOTE

rm 并不会从标准输入中读取参数

2022-03-29 17:36:33

NOTE

能以命令行参数的形式获取文件名,那该如何将先前运行过的命令(如 echo 或 ls)的输出放入命令行呢?

2022-03-29 17:36:57

NOTE

bash 的命令替换特性

2022-03-29 17:37:08

NOTE

也可以使用 xargs 命令

2022-03-29 17:37:15

NOTE

参见 15.13 中的讨论

2022-03-29 17:37:18

NOTE

子 shell 中运行的

2022-03-29 17:37:25

NOTE

可以用 rm -i 降低风险,选项 -i 会提醒你确认每次删除。对于小批量文件来说,这么做没毛病,但如果文件数量众多,那整个过程可就冗长不堪了

2022-03-29 17:38:44

NOTE

用 !! 来确保更加万无一失

2022-03-29 17:39:08

2.18 在一行中多次重定向

NOTE

也就是说,shell 脚本 divert 可以将其输出定向到不同的描述符,调用程序(invoking program)可以将描述符指向不同的目标

2022-03-29 17:40:37

NOTE

如果 divert 是一个 C 程序的可执行文件,则无须任何 open() 调用就可以向文件描述符3、4、5、6 写入

2022-03-29 17:40:53

NOTE

标准输入是 0,标准输出是 1,标准错误是 2

2022-03-29 17:41:40

NOTE

如果不指定数字,则假定为 1。这意味着可以用略显啰唆的写法 1>(而不是简单的 >)跟上文件名来重定向标准输出,不过其实没这个必要,便捷写法 > 就挺好

2022-03-29 17:41:52

NOTE

可以在 shell 中打开任意数量的文件描述符,令其指向各种文件,这样一来,随后在命令行上调用的程序就不用再费事了,直接就可以使用这些已打开的文件描述符

2022-03-29 17:42:00

2.19 重定向不起作用时保存输出

NOTE

Unix 和 Linux 中的每个进程通常一开始都有 3 个已打开的文件描述符:一个用于输入(标准输入 STDIN),一个用于输出(标准输出 STDOUT),一个用于错误消息(标准错误STDERR)

2022-03-29 17:42:48

NOTE

至于是否遵循这种约定,将错误消息写入标准错误,将正常输出写入标准输出,那真的就得看程序员了

2022-03-29 17:56:09

NOTE

每个文件描述符都由一个数字(从 0 开始)表示。标准输入是 0,标准输出是 1,标准错误是 2。这意味着可以用略显啰唆的写法来重定向标准输出:1>(而不是简单的 >)跟上文件名,但其实没这个必要,便捷写法 > 就够了。要想重定向标准错误,可以使用 2>

2022-03-29 18:08:49

NOTE

前者是缓冲式的(buffered),后者是非缓冲式的(unbuffered)

2022-03-29 18:10:58

NOTE

非缓冲意味着每个字符都是单独写入,不会被收集在一起,然后再批量写入。也就是说,你可以立刻看到错误消息,发生故障时丢失此类消息的可能性较小,但由此带来的就是效率问题

2022-03-29 18:12:05

NOTE

如果想要同时保存并查看输出,该怎么办?2.16 节中讨论过的 tee 命令此时就能派上用场了

2022-03-29 18:12:20

NOTE

此时标准输出指向的是屏幕

2022-03-29 18:16:49

NOTE

因此,只有标准输出消息会出现在文件中,而错误消息仍旧会出现在屏幕上

2022-03-29 18:16:23

NOTE

bash 对此做了特殊处理,它可以识别出标准输出连接到了管道 3。因此,当你写出 2>&1 时,bash会假定你希望将标准错误也连入管道,尽管 2>&1 的正常处理方式并非如此

2022-03-29 18:17:20

NOTE

3命令行中的管道会先于重定向进行处理。详见 Bash Reference Manual 的 3.2.2 节

2022-03-29 18:17:26

NOTE

这种做法(包括一般的管道语法)带来的另一个结果是,我们无法只将标准错误(而非标准输出)传入其他命令,除非事先交换文件描述符

2022-03-29 18:17:37

2.20 交换STDERR和STDOUT

NOTE

STDERR和STDOUT

2022-03-29 18:17:46

NOTE

第三个文件描述符交换 STDERR 和 STDOUT:

2022-03-29 18:18:13

NOTE

每次重定向文件描述符时,就会将打开的描述符复制到另一个描述符

2022-03-29 18:18:35

NOTE

将语法 3>&1 读作“使文件描述符 3 指向与标准输出文件描述符 1 相同的值”。

2022-03-29 18:26:51

2.21 避免意外覆盖文件

NOTE

发现将输出重定向到了原本打算保存的文件,这种事情太常见了

2022-03-29 18:28:00

NOTE

noclobber 选项告诉 bash 在重定向输出时不要覆盖任何现有文件

2022-03-29 18:28:12

2.22 有意覆盖文件

NOTE

使用 >| 重定向输出。即便是设置了 noclobber,bash 也会忽略该选项,并覆盖文件

2022-03-29 18:28:39

NOTE

noclobber 的使用并不会代替文件权限。不管有没有使用 >|,如果没有目录的写权限,那么就无法创建文件

2022-03-29 18:30:14

NOTE

不管有没有使用 >|,你必须拥有文件的写权限才能覆盖现有文件

2022-03-29 18:30:09

NOTE

据 Chet 所说,“POSIX 指定了语法 >|,这还是从 ksh88 中选出来的。我也说不清楚 Korn5 为什么会选用这种写法。csh 用的是 >!”。为了帮助记忆,你可以将其视为一种强调。它的用法在英语中带有祈使语气,符合要求 bash 在必要时“无论如何”都要覆盖文件的意味。vi 和 ex 编辑器在其 write 命令中也用 ! 表达了相同含义(:w!filename)。如果没有 !,覆盖已有文件时,编辑器会发出抱怨。要是加上 !,就相当于告诉编辑器“做就行了!”

2022-03-29 18:30:39

第 3 章 标准输入

3.1 从文件获取输入

NOTE

它们可以从视觉上提示重定向的方向

2022-03-26 23:35:37

NOTE

cat

2022-03-26 23:43:11

NOTE

,但这是 shell 脚本编程的一项重要特性(DOS 命令行也借鉴了),对 shell 的功能性和简单性必不可少

2022-03-26 23:42:58

3.2 将数据与脚本存放在一起

NOTE

<<(here-document)从命令行而非文件重定向输入文本

2022-03-30 16:38:59

NOTE

grep 命令查找第一个参数是否在指定文件中出现,如果没有指定文件,那么它会在标准输入中查找。

2022-03-30 16:39:26

NOTE

设置 here-document,告诉 shell 将标准输入重定向(临时)到此处

2022-03-30 16:39:44

NOTE

EOF 是一个任意的字符串(你想用什么都行),用作临时输入的终止符。它并不属于输入的一部分,只是作为标记告诉输入在哪里结束

2022-03-30 16:40:51

NOTE

grep 命令加入 -i 选项,以示搜索时不区分大小写。这样便可以使用 grep -i $1<<EOF同时搜索“Bill”或“bill”

2022-03-30 16:41:04

3.3 避免here-document中的怪异行为

NOTE

足以告诉 bash 你希望区别处理 here-document 中的内容

2022-03-30 16:58:02

NOTE

here-document 的每一行都要执行参数扩展、命令替换以及算术扩展”

2022-03-30 16:58:12

NOTE

1,随后跟着两个 0。这就是为什么我们在搜索“pete”时,得到的是 pete00

2022-03-30 16:58:31

NOTE

搜索“bill”时,得到的是 bill00

2022-03-30 16:58:38

NOTE

bash 就知道不用执行扩展,这样就符合我们的预期行为了

2022-03-30 16:58:48

NOTE

扩展操作

2022-03-30 16:58:55

NOTE

在结尾处的 EOF 标记中出现的拖尾空白字符(哪怕只是一个空格)会导致无法将其识别为结束标记。bash 会吞掉脚本的剩余部分,将其也视为输入并继续查找 EOF。所以,一定要确保 EOF 之后没有额外的空白字符(尤其是空格或制表符)。

2022-03-30 16:59:15

3.4 缩进here-document

NOTE

弄乱 shell 脚本的格式

2022-03-30 17:00:23

NOTE

就像结尾处的 EOF 标记中出现的任何拖尾空白字符都会导致其无法被识别为结束标记一样(参见 3.3 节中的警告部分),使用除制表符外的前导字符也会造成同样的后果。如果你的脚本使用空格或混合空格和制表符来进行缩进,可别在here-document 中这么做。要么使用制表符,要么什么都不用。另外,小心有些文本编辑器会自动将制表符替换成空格。

2022-03-30 17:13:11

3.5 获取用户输入

NOTE

取用户输入并将其保存在 shell 变量 REPLY中,这是 read 的最简形式

2022-03-30 17:22:59

NOTE

记住,要在提示信息结尾处加上标点符号或空格,因为光标会停在那里等待输入

2022-03-30 17:23:19

NOTE

t 选项可以设置超时值。指定秒数达到后,不管用户是否输入,read语句都会返回。我们的示例同时用到了 -t 和 -p 选项,但你也可以单独使用 -t 选项。从 bash 4 开始,你甚至可以将超时值指定为小数,如 .25 或 3.5。如果读取超时,则退出状态码($?)将大于 128

2022-03-30 17:23:31

3.6 获取yes或no

NOTE

尤其是不用区分大小写,如果用户直接按回车键,还能提供默认值

2022-03-30 17:23:56

NOTE

11.7 节中给出了该问题的另一种处理方法

2022-03-30 17:34:07

3.7 选择选项列表

NOTE

select 语句能够轻松地在 STDERR 上为用户生成编号列表,以便用户从中做出选择。虽然按下 Ctrl-D 可以结束 select,空输入会再次输出菜单,但别忘了提供“退出”或“结束”选项

2022-03-30 17:39:35

3.8 提示输入密码

NOTE

从用户那里读取到的输入行保存在变量 $PASSWD 中。

2022-03-30 17:40:04

NOTE

如果禁止了回显功能,当用户按下回车键时,就不会回显换行符,后续输出就会和提示信息出现在同一行

2022-03-30 17:41:48

NOTE

密码在内存中是以明文形式存放的,有可能通过核心转储或 /proc/core(如果你所用的操作系统提供了 /proc/)访问到。在多进程环境中也是如此,其他进程也有可能读取到密码。可能的话,最好使用 SSH 证书。无论任何情况,明智的做法是假定系统中的 root 和其他可能的用户都能接触到密码并对其进行相应的处理

2022-03-30 17:42:31

NOTE

用 stty -echo 来禁止输入密码时的屏幕回显。这么做的问题在于如果脚本意外终止,那么回显仍会处于关闭状态。有经验的用户知道输入 stty sane 将其恢复,但这并非人人都懂

2022-03-30 17:42:46

第 4 章 执行命令

4.1 运行程序

NOTE

从哪里运行程序呢?

2022-03-31 15:06:53

NOTE

bash 使用名为 PATH 变量包含了一个目录列表。各个目录之间以冒号(:)分隔。bash 在这些目录中查找命令行上指定的可执行文件。目录的顺序很重要:bash 按照变量中所列出的目录顺序依次查找,选择所找到的第一个同名的可执行文件。

2022-03-31 15:07:01

NOTE

很多人觉得将点号放进 $PATH 是一种很大的安全风险:别有用心的人会欺骗你执行同名命令的恶意版本(如 ls)。如果将点号放在目录列表的最前面,那么他人的 ls 版本的优先级就高于正常的 ls,你可能不知不觉间就执行了前者

2022-03-31 15:34:02

NOTE

如果将点号作为 $PATH 变量中的最后一个目录,至少不会那么容易被骗。当然,要是压根就不将它列入目录列表,那肯定更安全,而且你仍然可以执行当前目录中的命令,在前面加上点号和斜线即可

2022-03-31 15:53:40

NOTE

绝不要将点号目录或可写目录放进 root 的 $PATH 变量中。有关该话题的更多信息,参见 14.9 节和 14.10

2022-03-31 15:54:04

NOTE

一些 bash 用户的常见做法是创建个人 bin 目录,这类似于保存可执行文件的系统目录/bin 和 /usr/bin。你可以将自己喜欢的 shell 脚本和其他定制或私有命令放入个人的bin 目录中(如果创建于主目录,则路径为~/bin)。然后将该目录加入 PATH)。这样一来,你既可以拥有自己偏好的定制工具,也不存在误执行陌生人命令的安全风险。

2022-03-31 15:54:21

4.2 依次执行多个命令

NOTE

依次执行每个命令

2022-03-31 15:55:25

4.3 同时执行多个命令

NOTE

你可以在命令末尾添加一个 & 符号,在后台运行该命令。这样一来,就能够连续快速地执行这 3 个命令

2022-03-31 15:56:10

NOTE

在“后台”(Linux 其实没有这么个地方)运行某个命令时,真正的意思是我们断开了键盘输入与命令之间的联系,shell 在显示命令行提示符并接受更多命令输入前不会再等着该命令完成。命令输出(除非我们采取明确的操作来改变这种行为)仍然会出现在屏幕上,因此,示例中 3 个命令的输出会在屏幕上交错出现。

2022-03-31 15:58:22

NOTE

之后并未出现 & 符号,因此该命令不会在后台运行,bash 会等待其运行完毕才显示命令行提示符($)

2022-03-31 15:59:05

NOTE

作业号或进程 ID 可用于对作业实施有限的控制。例如,我们可以用 kill %1(因为作业号为 1)或者指定进程 ID(kill 4592)来“杀死”long 作业,二者能够实现相同的结果

2022-03-31 15:59:13

NOTE

你也可以用作业号重新连接到后台作业。例如,可以用 fg %1 将 long 作业放回前台。如果后台只有一个作业在运行,甚至都不用指定作业号,只使用 fg 即可

2022-03-31 15:59:26

NOTE

Ctrl-Z 暂停该命令,返回到提示符下。接着输入 bg 来恢复作业,并在后台继续运行。这么做的效果相当于事前在命令尾部加上 & 符号

2022-03-31 15:59:36

4.4 了解命令是否成功运行

NOTE

shell 变量 $? 中保存着命令的退出状态,其取值范围为 0~255。在编写 shell 脚本时,良好的做法是:如果一切正常,脚本退出时就返回 0;如果运行过程中出错,则返回非 0值

2022-03-31 16:01:09

NOTE

我们推荐只使用 0~127 作为返回值,因为 shell 用 128+N 代表被信号 N“杀死”。另外,如果使用的值大于 255 或小于 0,则会出现值回绕。可以用 exit 语句(如exit 1 或 exit 0)返回退出状态。但要注意,读取命令退出状态的机会只有一次

2022-03-31 16:01:29

NOTE

输入 echo $? 时,结果是 1,这是 badcommand 的返回值。但是该 echo 命令本身是成功执行的,因此最新的退出状态就是 0(表示成功)。因为检查退出状态的机会只有一次,所以很多 shell 脚本会立即将退出状态保存到其他 shell 变量中

2022-03-31 16:02:04

NOTE

bash 的一大特点是,脚本语言与在终端窗口提示符下键入的命令完全相同。在编写脚本时,这使得检查语法和逻辑要容易得多

2022-03-31 16:02:25

NOTE

(( )) 对算术表达式进行求值;参见 6.1 节和 6.2 节。

2022-03-31 16:02:38

4.5 仅当一个命令运行成功后才执行下一个命令

NOTE

写脚本就是另一回事了,对于示例中这样的脚本,进行测试是非常有必要的,这可以确保不会意外地将所在目录中的文件全部删除。

2022-03-31 16:04:50

NOTE

C 语言程序员会将其作为提供给 exit() 函数的参数值,例如,exit(4); 会返回 4。对于 shell 而言,退出码 0 代表成功,非 0 则代表失败

2022-03-31 16:05:16

4.6 减少if语句的数量

4.7 无人值守下运行耗时作业

NOTE

无人值守下运行耗时作业

2022-03-31 16:11:39

NOTE

如果想在后台运行作业并在该作业完成前退出 shell,那就需要对作业使用 nohup。

2022-03-31 16:12:04

NOTE

将作业置入后台时(通过 4.3 节中介绍过的 &),它仍旧是 bash shell 的子进程。如果退出 shell 的某个实例,bash 就会向其所有子进程发送 hangup 信号。这就是作业运行不了多久的原因。只要退出 bash,后台作业就会被“杀死”

2022-03-31 16:12:27

NOTE

nohup 命令只是设置子进程忽略 hangup 信号。你仍可以用 kill 命令“杀死”作业,因为 kill 发送的是 SIGTERM 信号,而非 SIGHUP 信号。但有了 nohup,作业就不会在退出bash 时被无意间“杀死”

2022-03-31 16:12:38

NOTE

nohup 给出的那句关于追加输出的消息只是为了提高自身的实用性。因为你有可能发出nohup 命令后就退出 shell,输出信息也就无处可去了,也就是说,终端中的 bash 会话已经结束,作业无法再向 STDOUT 写入。更重要的是,向不存在的位置写入信息会产生错误。因此,nohup 会替你重定向输出,将其追加(不是覆盖,而是添加到文件现有内容的末尾)到当前目录下的 nohup.out 文件中。你也可以明确地在命令行上指定将输出重定向到其他地方,nohup 足够聪明,能够发现你已经另有安排,也就不会再使用nohup.out 了

2022-03-31 16:12:55

4.8 出现故障时显示错误消息

NOTE

惯用做法是配合使用 || 和命令来输出调试 / 错误消息

2022-03-31 16:17:14

NOTE

注意,最后一个命令必须以分号结尾,闭合花括号与其中的内容之间要用空白字符分隔。2 具体的讨论参见 2.14 节。

2022-03-31 16:21:01

4.9 执行变量中的命令

NOTE

这里要给出一种略有不同的方法,它揭示了 bash 自身的一些东西。我们不仅可以将变量内容(详见第 5 章)用于参数,还可以用于命令本身。

2022-03-31 16:21:58

NOTE

注意你所使用的变量名。有些程序(如 InfoZip)使用环境变量(如 UNZIP)向自身传递设置,如果你做了类似 ZIP=/usr/bin/zip 的操作,少不了要抓耳挠腮上几天,搞不明白为什么该程序可以在命令行上正常运行,偏偏在脚本中就不行了。相信我们,这都是从惨痛教训中总结出的经验。另外,好好读读手册。

2022-03-31 16:22:48

4.10 执行目录中的所有脚本

NOTE

如果匹配到的是文件(由 -f 测试)且具有执行权限(由 -x 测试),那么 shell 会尝试执行此脚本

2022-03-31 16:25:02

第 5 章 脚本编程基础:shell变量

NOTE

变量(存放字符串和数值的容器,可以进行修改、比较、传递)

2022-03-31 16:27:33

NOTE

bash 脚本中的变量名称通常采用全大写,但这并非强制性的,只是一种常见做法而已。变量不用事先声明,直接使用就行了。变量基本上都是字符串类型,不过有些运算符能够将变量内容视为数字

2022-03-31 16:27:58

NOTE

首先,赋值语法 name=value 看起来相当直观,但 = 两侧不能有任何空白字符

2022-03-31 16:28:24

NOTE

shell 的主要目的是执行命令:你在命令行上指定要执行的命令。命令名之后的任何单词都会作为参数传给该命令。

2022-03-31 16:28:55

NOTE

如果允许 = 两侧出现空白字符

2022-03-31 16:29:04

NOTE

此时 shell 很难区分出到底是要调用命令(如本例中的 ls)还是要给变量赋值

2022-03-31 16:29:20

NOTE

shell 不允许在 = 两侧出现空白字符。该规定的另一方面也值得注意:不要在文件名中使用 =,对于 shell 脚本尤为如此(尽管可行,但不推荐)

2022-03-31 16:30:09

NOTE

出现在表达式 $(( )) 中的变量是个例外

2022-03-31 16:30:24

NOTE

用编译器的行话来说,赋值和检索值在语法上的差异就是变量的左值(L-value)与右值(R-value)之间的差异(赋值运算符的左侧和右侧)

2022-03-31 16:30:34

5.1 记录脚本

NOTE

记录脚本

2022-04-25 00:54:21

NOTE

有些人将 shell 语法、正则表达式,以及 shell 脚本编程的其他部分描述为只写(write-only)语法,以此暗示很多 shell 脚本中难以理解的错综复杂之处

2022-03-31 16:31:21

NOTE

最好的方法就是该用注释的地方就用注释(另一种方法是使用有意义的变量名)。在奇怪的语法或紧凑的表达式前加上注释的确有

2022-03-31 16:31:42

5.2 在shell脚本中嵌入文档

NOTE

在shell脚本中嵌入文档

2022-04-25 00:54:26

NOTE

使用内建命令 :(空命令)和 here-document 在脚本中嵌入文档

2022-03-31 16:34:32

NOTE

任何纯文本文档或标记都可以像这样使用,要么散布在脚本内,要么集中放在脚本末尾,后一种做法会更好些。考虑到安装了 bash 的计算机系统可能也安装了 Perl,POD格式也许是不错的选择。Perl 通常包含 pod2* 程序,可用于将 POD 转换成 HTML、LaTeX、手册页、文本以及用法文件

2022-03-31 16:36:28

NOTE

Perl Best Practices(O’Reilly 出版)一书中有一些优秀的库模块和应用程序文档模板,可以轻松地转换包括纯文本在内的任何文档格式。参见本书示例归档文件中的 CODE/ch07/Ch07.001_Best_Ex7.1 和CODE/ch07/Ch07.001_Best_Ex7.2

2022-03-31 16:44:25

5.3 提高脚本可读性

NOTE

提高脚本可读性

2022-04-25 00:54:31

NOTE

记录意图,而不是代码中的繁枝末节。如果遵循余下的要点,则代码应该会相当清晰。写代码时,编制提醒、提供数据布局示例或标题,记下脑子里想到的方方面面的细节。如果实现过程比较微妙或难懂,代码本身也要加以说明

2022-03-31 16:54:44

NOTE

可以用垂直空白字符创建功能相似的代码块。当然,对函数也是如此

2022-03-31 16:55:15

NOTE

我们敢保证,当不得不修正或改动脚本时,你绝对会在某个地方搭进去 10 倍或 100 倍的时间

2022-03-31 16:57:23

NOTE

对于比较长的行,选择在 76 个字符左右的位置处断行。没错,我们知道大多数屏幕(更准确地说,应该是终端程序)能处理的字符个数远超于此,但是 80 个字符宽度的纸张和屏幕仍是默认设置,而且在代码右侧留些空白绝对没什么坏处。不断往右边拖滚动条或看到屏幕上那些丑陋的折行,绝不是一件令人愉悦的事情,还容易让人分神。可别这么做。

2022-03-31 16:57:54

NOTE

在某些情况下,(可能通过 SSH)将生成的行发往别处时,断行会得不偿失。但大部分情况下,这么做是值得的

2022-03-31 16:58:05

5.4 将变量名与周围的文本分开

NOTE

将变量名与周围的文本分开

2022-04-25 00:54:36

NOTE

实际上,有人认为坚持使用花括号是一种好习惯,这样就不用考虑什么时候该用,什么时候不用,还能在整个脚本中保持写法一致。也有人觉得这样需要敲入的可用可不用的字符太多了,不但不好输入,还会让代码看起来很繁杂。说到底,这还是个人喜好问题

2022-03-31 17:04:41

5.5 导出变量

NOTE

导出变量

2022-03-31 17:09:33

NOTE

有时候,两个脚本互不知道对方的变量是件好事。如果在一个脚本的 for 循环中调用了另一个 shell 脚本,那么你肯定不希望这个脚本将 for 循环弄得乱七八糟(这种事不大可能发生,因为该脚本几乎肯定是在子 shell 中运行,这里只是举例说明)

2022-03-31 17:09:42

NOTE

敲入命令 env(或者内建命令 export -p)就能列出各个变量及其值

2022-03-31 17:13:05

NOTE

导出的变量实际上是按值调用的。在被调用脚本中修改导出变量的值并不会改变调用脚本中该变量的值

2022-03-31 17:13:40

NOTE

如何将被调用脚本修改过的值传回来?”答案是:做不到

2022-03-31 17:13:50

NOTE

你只能设计脚本时避开这种需求。有什么办法能够应对这种限制吗?方法之一是让被调用的脚本自己输出修改过的值,然后调用脚本读取该输出。例如,某个脚本导出了变量 VAL。要想在调用脚本中得到 $VAL 的新值,就得将修改后的值写入标准输出,然后获取并重新赋值

2022-03-31 17:14:03

NOTE

(有关 $() 语法的更多讲解,参见 10.5 节。)

2022-03-31 17:14:10

5.6 查看所有的变量值

NOTE

查看所有的变量值

2022-04-25 00:54:48

NOTE

用 set 命令查看当前 shell 中的所有变量值以及函数定义。用 env(或 export -p)命令查看那些被导出的、可用于子 shell 的变量。在 bash 4 或更高版本中,也可以使用 declare -p 命令。

2022-03-31 17:15:34

NOTE

declare 语句形式的输出可以在 shell 脚本中作为源代码,重新创建这些变量并为其赋值。各个选项(-i、-x、-r、-a)分别指明了变量为整数类型、已经导出、只读、数组类型

2022-03-31 17:15:57

5.7 在shell脚本中使用参数

NOTE

在shell脚本中使用参数

2022-04-25 00:54:43

NOTE

单个数位的数字用不着花括号,除非要区分变量名与其后出现的文本

2022-04-24 23:42:45

NOTE

但如果涉及 10 理解为 ${1} 后面紧跟着字符串 0

2022-04-24 23:42:55

5.8 遍历传入脚本的参数

NOTE

遍历传入脚本的参数

2022-04-25 00:54:54

NOTE

$* 引用的是命令行上出现的所有参数

2022-03-31 17:18:39

NOTE

如果文件名中不包含空格,该脚本则万事大吉,但有时难免碰上带有空格的文件名

2022-03-31 17:19:24

5.9 处理包含空格的参数

NOTE

处理包含空格的参数

2022-04-25 00:54:58

NOTE

bash 得到了一个包含 3 个单词的文件名,并将 ls 命令中的 $1 替换成了该文件名。到目前一切都还好。但是,我们并没有将脚本中的变量引用放入引号,因此 ls将文件名中的各个单词视为单独的参数(作为单独的文件名)

2022-03-31 17:20:26

5.10 处理包含空格的参数列表

NOTE

报错的原因与 for 循环中使用的 @

2022-03-31 17:23:59

NOTE

你得改用 shell 变量 * 和 $& 没什么两样

2022-03-31 17:29:28

5.11 统计参数数量

NOTE

统计参数数量

2022-04-25 00:55:02

NOTE

if 测试所提供的参数数量(保存在 $# 中)是否大于 3

2022-03-31 17:30:23

NOTE

错误信息会被重定向到标准错误。这种做法符合标准错误的本意:作为所有错误信息的通道。

2022-03-31 17:30:42

NOTE

该脚本还会根据检测到的错误返回不同的值。尽管这里没什么意义,但对于可能会被其他脚本调用的脚本而言,还是有用处的,这样就拥有了一种程序化的方法,不仅能够检测故障(非 0 的退出值),还可以区分不同的错误类型。

2022-03-31 17:30:54

NOTE

{#}、{#VAR}、${VAR#alt} 都在花括号里用到了 #,就把三者搞混了。第一种语法可以获得参数的数量,第二种语法可以获得变量 VAR 所保存值的长度,最后一种语法会执行某种替换操作。

2022-03-31 17:33:12

5.12 丢弃参数

NOTE

丢弃参数

2022-04-25 00:55:14

NOTE

如何在保留 for 循环的同时添加一个能够关闭文件名显示的选项呢?

2022-03-31 17:33:47

NOTE

用 shift 删除处理过的参数

2022-03-31 17:33:52

NOTE

添加了标记变量 2 变成 3 变成 1)

2022-03-31 17:34:26

NOTE

当 for 循环启动时,参数列表($@)中就再也没有 -v,剩下的是紧随其后的那些参数

2022-03-31 17:35:15

NOTE

按照惯例,shell 脚本的选项应该不区分位置,也就是说,myscript -a -p 应该等同于 myscript -p -a。而且,稳健的脚本还应该能处理重复选项,要么忽略,要么报错

2022-03-31 17:35:34

5.13 获取默认值

NOTE

获取默认值

2022-04-25 00:55:16

NOTE

:- 运算符的意思是,如果指定参数(这里是 1、3等),但后者是最常用到的。

2022-04-02 20:45:27

5.14 设置默认值

NOTE

设置默认值

2022-04-25 00:55:18

NOTE

示例中所引用的 HOME,随后再引用 $HOME 的话,返回的就是这个新值。

2022-04-02 20:46:42

NOTE

赋值运算符有一个重要的例外:不能对位置参数(如 *)赋值。在这种情况下,可以使用 :-(如 ${1:-default}),该表达式只返回值,但不进行赋值

2022-04-02 20:47:29

NOTE

顺便说一下,注意 {VAR:-value} 在形式上的差异,也许可以帮助你记忆这两种让人抓狂的符号。:= 执行赋值操作,同时返回运算符右侧的值。:- 只做了前者一半的工作:返回值,但不赋值。因此,它的符号也只有等号的一半(一个横杠,而不是两个

2022-04-02 20:47:49

5.15 使用空值作为有效的默认值

NOTE

使用空值作为有效的默认值

2022-04-25 00:55:23

NOTE

这个示例将 HOME 的内容。但如果删除该变量,则会发生替换。要想允许出现空字符串,使用不带冒号的 = 即可。但大部分时候是使用 :=,因为无论你是否有意,空值基本上没什么用处。

2022-04-25 00:08:59

5.16 不只使用字符串常量作为默认值

NOTE

不只使用字符串常量作为默认值

2022-04-25 00:55:26

NOTE

我们可以在该运算符(以及其他类似运算符)右侧做些什么?bash 手册页对于出现在运算符右侧的内容是这么表述的:“……要经过波浪号扩展、参数扩展、命令替换以及算术扩展”。

2022-04-25 00:09:51

NOTE

参数扩展意味着可以使用其他变量,如 {BASE:={HOME}}。波浪号扩展意味着可以使用 ~bob 这样的表达式,它会扩展成用户 bob 的主目录。可以通过 (cmds)。算术扩展意味着可以用 $(( )) 语法执行整数算术运算。例如:

2022-04-25 00:09:53

5.17 对不存在的参数输出错误消息

NOTE

对不存在的参数输出错误消息

2022-04-25 00:55:30

NOTE

因为无法控制错误消息的这部分内容,而且这看起来好像是 shell 脚本自身出现的错误,再加上可读性的问题,该技术在商业级的 shell 脚本中并不多见。(不过确实有助于调试。)

2022-04-25 00:52:48

NOTE

要想所有变量都具备这种行为,同时又不想逐个改动,可以使用 set -u 命令,“在变量替换时,将不存在的变量视为一种错误”。

2022-04-25 00:53:01

5.18 修改部分字符串

NOTE

修改部分字符串

2022-04-25 00:55:37

NOTE

为了避免文件名中包含空格,我们要将其放入引号。

2022-04-25 00:56:05

5.19 获得某个数的绝对值

5.20 用bash实现basename

5.21 用bash实现dirname

5.22 选取CSV的替换值

5.23 使用数组变量

5.24 转换大小写

5.25 转换为驼峰命名法

第 6 章 shell逻辑与算术

6.1 在shell脚本中执行算术操作

6.2 条件分支

6.3 测试文件特性

6.4 测试多个特性

6.5 测试字符串特性

6.6 测试等量关系

6.7 用模式匹配进行测试

6.8 用正则表达式测试

6.9 用重定向改变脚本行为

6.10 循环一段时间

6.11 在循环中使用read

6.12 循环若干次

6.13 在循环中使用浮点值

6.14 多路分支

6.15 解析命令行参数

6.16 创建简单的菜单

6.17 修改简单菜单的提示符

6.18 创建简单的RPN计算器

6.19 创建命令行计算器

第 7 章 中级shell工具

7.1 在文件中查找字符串

7.2 只显示包含搜索结果的文件名

7.3 了解搜索是否成功

7.4 不区分大小写搜索

7.5 在管道中进行搜索

7.6 缩减搜索结果

7.7 搜索更复杂的模式

7.8 搜索SSN

7.9 搜索压缩文件

7.10 保留部分输出

7.11 仅保留部分输入行

7.12 颠倒每行的单词

7.13 汇总数字列表

7.14 用awk统计字符串出现次数

7.15 用bash统计字符串出现次数

7.16 用便捷的直方图展示数据

7.17 用bash轻松实现直方图

7.18 显示匹配短语之后的文本段落

第 8 章 中级shell工具(续)

8.1 输出排序

8.2 数字排序

8.3 IP地址排序

8.4 提取部分输出

8.5 删除重复行

8.6 压缩文件

8.7 解压文件

8.8 检查tar归档文件中不重复的目录

8.9 转换字符

8.10 将大写字母转换为小写字母

8.11 将DOS文件转换为Linux格式

8.12 删除智能引号

8.13 统计文件的行数、单词数或字符数

8.14 重新编排段落

8.15 你不知道的less

第 9 章 查找文件:find、locate、slocate

9.1 查找所有的MP3文件

9.2 处理文件名中的怪异字符

9.3 提升已找到文件的处理速度

9.4 跟随符号链接查找文件

9.5 查找文件时不区分大小写

9.6 按日期查找文件

9.7 按类型查找文件

9.8 按大小查找文件

9.9 按内容查找文件

9.10 快速查找现有文件及其内容

9.11 在可能的位置上查找文件

第 10 章 脚本编程的附加特性

10.1 脚本“守护进程化”

10.2 代码重用

10.3 在脚本中使用配置文件

10.4 定义函数

10.5 使用函数:参数和返回值

10.6 中断陷阱

10.7 用别名重新定义命令

10.8 避开别名和函数

10.9 计算已过去的时间

10.10 编写包装器

第 11 章 处理日期和时间

11.1 格式化日期显示

11.2 提供默认日期

11.3 自动生成日期范围

11.4 将日期和时间转换为纪元秒

11.5 将纪元秒转换为日期和时间

11.6 用Perl获得昨天或明天的日期

11.7 日期与时间运算

11.8 处理时区、夏令时和闰年

11.9 用date和cron在第N天运行脚本

11.10 输出带有日期的日志

第 12 章 帮助最终用户完成任务的shell脚本

12.1 输出连字符

12.2 浏览相册

12.3 填装MP3播放器

12.4 刻录CD

12.5 比较文档

第 13 章 与解析相关的任务

13.1 解析shell脚本参数

13.2 解析参数时使用自定义错误消息

13.3 解析HTML

13.4 将输出解析到数组

13.5 用函数调用解析输出

13.6 用read语句解析文本

13.7 用read将输入解析至数组

13.8 读取整个文件

13.9 正确书写复数形式

13.10 一次提取一个字符

13.11 清理svn源代码树

13.12 用MySQL设置数据库

13.13 提取数据中的特定字段

13.14 更新数据文件中的特定字段

13.15 修剪空白字符

13.16 压缩空白字符

13.17 处理固定长度记录

13.18 处理没有换行的文件

13.19 将数据文件转换为CSV

13.20 解析CSV数据文件

第 14 章 编写安全的shell脚本

14.1 避开常见的安全问题

14.2 避免解释器欺骗

14.3 设置安全的$PATH

14.4 清除所有的别名

14.5 清除命令散列

14.6 防止核心转储

14.7 设置安全的$IFS

14.8 设置安全的umask

14.9 在$PATH中查找人皆可写的目录

14.10 将当前目录加入$PATH

14.11 使用安全的临时文件

14.12 验证输入

14.13 设置权限

14.14 密码被泄露到进程列表

14.15 编写setuid或setgid脚本

14.16 限制访客

14.17 使用chroot囚牢

14.18 以非root用户身份运行

14.19 更安全地使用sudo

14.20 在脚本中使用密码

14.21 使用无密码的SSH

14.22 限制SSH命令

14.23 断开非活跃会话

第 15 章 高级脚本编程

15.1 以可移植的方式查找bash

15.2 设置兼容POSIX工具的$PATH

15.3 开发可移植的shell脚本

15.4 用虚拟机测试脚本

15.5 使用可移植的循环

15.6 使用可移植的echo

15.7 仅在必要时分割输出

15.8 以十六进制形式查看输出

15.9 使用bash的网络重定向

15.10 查找自己的IP地址

15.11 从另一台机器获取输入

15.12 在脚本运行期间重定向输出

15.13 解决“Argument list too long”错误

15.14 向syslog记录脚本日志

15.15 正确地使用logger

15.16 在脚本中发送电子邮件

15.17 用阶段自动化进程

15.18 一心二用

15.19 在多个主机上执行SSH命令

第 16 章 bash的配置与自定义

16.1 bash启动选项

16.2 自定义提示符

16.3 在程序运行前出现的提示符

16.4 永久修改$PATH

16.5 临时修改$PATH

16.6 设置$CDPATH

16.7 当找不到命令时

16.8 缩短或修改命令名称

16.9 调整shell行为及环境

16.10 用.inputrc调整readline的行为

16.11 通过添加~/bin来存放个人工具

16.12 使用辅助提示符:PS3、$PS4

16.13 在会话间同步shell历史记录

16.14 设置shell的历史选项

16.15 创建更好的cd命令

16.16 一次性创建并切换到新目录

16.17 直达底部

16.18 用可装载的内建命令为bash添加新特性

16.19 改善可编程补全

16.20 正确使用初始化文件

16.21 创建自包含的可移植rc文件

16.22 自定义配置入门

第 17 章 内务及管理任务

17.1 批量重命名文件

17.2 在Linux中使用GUN Texinfo和info

17.3 批量解压ZIP文件

17.4 用screen恢复断开的会话

17.5 共享单个bash会话

17.6 记录整个会话或批量作业

17.7 注销时清除屏幕

17.8 获取用于数据恢复的文件元数据

17.9 为多个文件创建索引

17.10 使用diff和patch

17.11 统计文件间存在多少差异

17.12 删除或重命名名称中包含特殊字符的文件

17.13 将数据追加到文件开头

17.14 就地编辑文件

17.15 将sudo应用于一组命令

17.16 查找仅出现在一个文件中的行

17.17 保留最近的N个对象

17.18 写入循环日志

17.19 循环备份

17.20 搜索不包含grep进程自身在内的ps输出

17.21 确定某个进程是否正在运行

17.22 为输出添加前缀或后缀

17.23 对行进行编号

17.24 生成序列

17.25 模拟DOS的pause命令

17.26 为数值添加逗号

第 18 章 写得少,干得快

18.1 在任意目录之间快速移动

18.2 重复上一个命令

18.3 执行类似命令

18.4 快速替换

18.5 参数重用

18.6 名称补全

18.7 安全第一

18.8 修改多个命令

第 19 章 窍门与陷阱:新手常见错误

19.1 忘记设置可执行权限

19.2 修复“No such file or directory”错误

19.3 忘记当前目录不在$PATH中

19.4 将脚本命名为test

19.5 试图修改已导出的变量

19.6 赋值时忘记加引号

19.7 忘记模式匹配的结果是按字母顺序排列的

19.8 忘记管道会产生子shell

19.9 使终端恢复正常

19.10 用空变量删除文件

19.11 printf的怪异行为

19.12 测试bash脚本语法

19.13 调试脚本

19.14 使用函数时避免出现“command not found”错误

19.15 混淆了shell通配符和正则表达式

附录 A 参考

A.1 bash调用

A.2 自定义提示符字符串

A.3 ANSI颜色转义序列

A.4 内建命令

A.5 bash保留字

A.6 shell内建变量

A.7 set选项

A.8 shopt选项

A.9 测试运算符

A.10 I/O重定向

A.11 echo选项与转义序列

A.12 printf

A.13 用strftime格式化日期和时间

A.14 模式匹配字符

A.15 extglob扩展模式匹配运算符

A.16 tr转义序列

A.17 readline的init文件语法

A.18 Emacs模式命令

A.19 vi控制模式命令

A.20 ASCII编码表

附录 B bash 自带的示例

bash文档和示例

附录 C 命令行处理

C.1 命令行处理步骤

C.2 引用

C.3 eval

附录 D 修订控制

D.1 参考

D.2 Git

D.3 Bazaar

D.4 Mercurial

D.5 Subversion

D.6 Meld

D.7 etckeeper

D.8 其他

附录 E 从源代码构建 bash

E.1 获得bash

E.2 解开归档文件

E.3 归档文件中都有什么

E.4 如何获得帮助

关于作者

关于封面

看完了