关于本书
本书讲述的是 bash(GNU Bourne Again Shell),它是 shell 家族的一员,这个大家族中包括了最初的 Bourne shell(sh)、Korn shell(ksh)和 public domain Korn shell(pdksh)。虽然它们以及其他一些 shell(如 dash 和 zsh)并没有重点讨论,但大部分脚本在这些 shell 中运行得还不错。
你既可以逐页阅读,也可以选择性翻阅。最重要的是,我们希望在你不知道如何上手或需要提示时,能够从中轻松地找到正确(或者接近正确)的答案,为你省时省力。
Unix 哲学的很大一部分就是构建各种只专注于做好一件事的简单工具,然后根据需要组合这些工具。这种组合过程经常是通过 shell 脚本实现的,因为这些称为管道的命令要么很长,要么难以记忆和输入。在适合的情况下,我们会在 shell 脚本的背景下讲述大量此类工具的用法,将其作为各个部分的黏合剂,以达成最终的目标。
本书第 1 版使用的写作工具是 OpenOffice.org Writer,根据情况运行在 Linux 或 Windows 主机上,书稿全都用 Subversion 保存(参见附录 D)。开放文档格式的性质为写作过程的诸多重要方面带来了便利,例如交叉参考和代码提取(参见 13.18 节)。源文档随后转换成了用于出版的 DocBook 格式。
本书(第 2 版)转向了 O'Reilly Atlas 系统上的 Asciidoc 和 Git,效果非常好。十分感谢 O'Reilly 的产品与工具部门所提供的帮助。
GNU软件
本书中讨论的 bash 以及其他很多软件是 GNU 项目的一部分。GNU(发音为 guh-noo,类似于 canoe)是“GNU's Not Unix”的递归式缩写,该项目可以追溯到 1984 年,其目标是开发出一款自由的类 Unix 操作系统。
这里不讲太多细节,常说的 Linux 其实指的是包含各种支持软件的内核。这个内核的周围还有各种 GNU 工具,根据你使用的发行版,可能还包括其他种类的软件。不过,Linux 内核本身并不是 GNU 软件。
GNU 项目认为 Linux 实际上应该称作“GNU/Linux”,这么说并不是没有道理,因而有些发行版(尤其是 Debian)也采用了这种叫法。因此,GNU 的目标照理说算是达到了,尽管结果算不上纯粹的 GNU。
GNU 项目贡献了大量卓越的软件,bash 就是其中的明星之一。书中讲到的每种工具差不多都有 GNU 版本,尽管 GNU 工具的特性更为丰富,(通常)也更友好,但有时在用法上存在少量差异。15.3 节会谈到这个问题,但 20 世纪 80 年代和 90 年代的商业 Unix 厂商对这些差异也负有很大责任。
有关 GNU、Unix 和 Linux 方方面面的讨论已经够多了(像本书这种厚度的著作就有好几本),不过我们觉得这样的简述也算差不多了。更多详情可以参阅 GNU 网站。
关于代码示例
当在本书中展示 shell 脚本的可执行代码时,我们通常会将其显示在一个缩进区域内,如下所示:
$ ls
a.out cong.txt def.conf file.txt more.txt zebra.list
$
第一个字符一般是美元符号($
),表示在 bash shell 提示符处输入命令。(记住,这个提示符是可以修改的,如 16.2 节中所述,这意味着你自己用的提示符可能和这里看到的大相径庭。)提示符由 shell 输出,行中余下部分由你输入。类似地,例子中的最后一行通常也是个提示符(还是 $
),表明命令执行完毕,控制权已经返回给 shell。
英镑符号或井字符(#
)就要复杂些了。在包括 bash shell 脚本在内的很多 Unix 或 Linux 文件中,开头的 #
表示注释,我们在一些代码示例中也采用了这种用法。但如果作为 bash 命令提示符的结尾符号(而非 $
),#
则表示当前的用户身份是 root。我们只有一个示例是以 root 身份执行的,应该不会造成什么困惑,但理解其中的含义还是很重要的。
如果示例中没有提示字符串,那么表明当前看到的是 shell 脚本的内容。对于一些比较长的示例,我们还会给脚本加上行号,但这些行号可不属于脚本。
我们偶尔也会以会话日志或命令序列的形式展示示例。有时候,为了让你看到示例或操作结果中的脚本或数据文件,我们会对一个或多个文件使用 cat
命令,如下所示:
$ cat data_file
static header line1
static header line2
1 foo
2 bar
3 baz
很多长脚本和函数也可以从网络下载,具体参见“使用示例代码”一节。只要条件允许,本书示例会选择使用 !/usr/bin/env bash
,这种写法的可移植性要比你在 Linux 或 Mac 中见到的 !/bin/bash
更好。详细内容参见 15.1 节。
另外,你也许会注意到不少代码示例中会有类似以下的内容:
# cookbook filename: snippet_name
这表示你当前所阅读的代码可以从本书的 GitHub 仓库中下载。代码都在形如 ./chXX/snippet_name 的文件中,其中的 chXX 指明了具体是哪一章,snippet_name 是文件名。
无谓的cat
一些 Unix 用户非常乐于指出其他用户代码中的低效之处。这种具有建设性的批评多是委婉提出的,对方也能欣然接受。
最常见的可能就是“无谓的 cat
”(useless use of cat award),如果有人写成 cat file | grep foo
,而不是简单地使用 grep foo file
,那可就算是“中奖”了。在这种情况下,cat
不仅没有必要,而且还会引发系统开销,因为它是在子进程中运行的。另一种常见用法是 cat file | tr '[A-Z]' '[a-z]'
,而非 tr '[A-Z]' '[a-z]' < file
。有时 cat
甚至会导致脚本运行失败(参见 19.8 节)。
但是……(你也知道下面该说什么了吧?)看似没必要的 cat
有时其实是刻意为之。它可以在演示管道的某部分时占据一个位置,随后用其他命令替换掉(甚至可能是 cat -n
)。也可以将文件名靠近代码左侧放置,相较于躲在书页最右侧的 <
之后的文件名,这更能清晰地吸引用户注意力。
我们赞赏高效,也认可这是要努力达成的目标,但其重要性已经不可与过去相提并论了。这绝不是在提倡漫不经心的态度,也不是鼓励代码膨胀,我们只是说这并不会立马就把 CPU 拖慢了。要是你喜欢 cat
,那就用吧。
关于Perl
除了几个确实有必要的示例,我们刻意在解决方案中尽可能地避免使用 Perl。无论是深度还是广度,本书中所讲到的 Perl 都远不及其他资料。相较于 shell 脚本,Perl 解决方案的代码量通常要多出不少,开销也大得多。shell 脚本和 Perl 脚本之间泾渭分明,而本书可是一本关于 shell 脚本编程的专著。
shell 脚本可以说是 Unix 程序之间的黏合剂,而 Perl 将 Unix 外部程序的大量功能融入了语言本身。这提高了 Perl 的效率,也改善了某些方面的可移植性,但所付出的代价就是用法上的差异以及难以高效地运行仍旧需要的外部程序。
究竟选择哪一种工具,这往往与熟悉程度有关。关键是完成工作,工具的选择是放在其次的。我们会向你展示 bash 及相关工具的多种用法。当需要搞定手头的工作时,你得挑选用什么工具。
更多资源
- Perl Cookbook, 2nd Edition,Nathan Torkington 和 Tom Christiansen 合著,O'Reilly 出版
- Programming Perl, 4th Edition,Larry Wall 等著,O'Reilly 出版
- Perl Best Practices,Damian Conway 著,O'Reilly 出版
- Mastering Regular Expressions, 3rd Edition,Jeffrey E. F. Friedl 著,O'Reilly 出版
- Learning the bash Shell, 3rd Edition,Cameron Newham 著,O'Reilly 出版
- Classic Shell Scripting,Nelson H. F. Beebe 和 Arnold Robbins 合著,O'Reilly 出版
- 本书网址链接请到图灵社区本书页面查看。