第一部分 热身

shell是一个命令解释器,是介于操作系统kernel与用户之间的一个绝缘层。准确的说,它也是一种强力的计算机语言。一个shell程序,被称为一个脚本,是一种很容易使用的工具,它可以通过将系统调用、公共程序、工具和编译过得二进制程序粘合在一起来建立应用。事实上,所有的UNIX命令和工具再加上公共程序,对于shell脚本来说,都是可调用的。如果这些你还觉得不够,那么shell内建命令,比如test和循环结构,也会给脚本添加强力的支持和灵活性。shell脚本对于管理系统任务和其他重复工作的例程来说,表现的非常好,根本不需要那些华而不实的成熟紧凑的程序语言。

第一章 为什么使用shell编程

没有程序语言是完美的,甚至没有一个唯一最好的语言。只有对于特定目的,比较合适和不合适的程序语言。

-- Herbert Mayer

对于任何想适当精通一些系统管理知识的人来说,掌握shell脚本知识都是最基本的,即使这些人可能并不打算真正的编写一些脚本。想一下Linux机器启动过程,在这个过程中,必将允许/etc/rc.d目录下的脚本来存储系统配置和建立服务。详细的理解这些启动脚本对于分析系统的行为是非常重要的,并且有时候可能必须修改它。

学习如何编写Shell脚本并不是一件很困难的事,因为脚本可以分为很小的块,并且相对于shell特性的操作和选项1部分,只需要学习很小的一部分就可以了。语法是简单而且直观的,编写脚本很像是在命令行上把一些相关命令和工具连接起来,并且只有很少的一部分规则需要学习。绝大部分脚本第一次就可以正常工作,而且即使调试一个长一些的脚本也是很直观的。

一个shell脚本是一个类似于小吃店(quick and dirty)的方法,在你使用原型设计一个很复杂的应用的时候。在工程开发的第一个阶段,即使从功能中取得很有限的一个子集放到shell脚本中来完成往往都是非常有用的。使用这种方法,程序的结果可以被测试和尝试运行,并且在处理使用诸如C/C++, JAVA或者Perl语言编写的最终代码前,主要的缺陷和陷阱往往就被发现了。

shell脚本遵循典型的UNIX哲学,就是把大的复杂的工程分成小规模的子任务,并且把这些部件和工具结合起来。许多人认为这种办法更好一些,至少这种办法比使用那些高、大、全的语言更美,更愉悦,更适合解决问题。比如Perl就是这种能干任何事情能适合任何人的语言,但是代价就是你需要强迫自己使用这种语言来思考和解决问题的方法。

什么时候不使用 Shell 脚本

  • 资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash 等等)
  • 需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算 (这种情况一般使用 C++或 FORTRAN 来处理)
  • 有跨平台移植需求(一般使用 C 或 Java) 复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)
  • 对于影响系统全局性的关键任务应用。 对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵,破解,恶意破坏等等。
  • 项目由连串的依赖的各个部分组成。
  • 需要大规模的文件操作
  • 需要多维数组的支持
  • 需要数据结构的支持,比如链表或数等数据结构
  • 需要产生或操作图形化界面 GUI
  • 需要直接操作系统硬件
  • 需要 I/O 或 socket 接口
  • 需要使用库或者遗留下来的老代码的接口
  • 私人的,闭源的应用(shell 脚本把代码就放在文本文件中,全世界都能看到)

如果你的应用符合上边的任意一条,那么就考虑一下更强大的语言吧--或许是 Perl,Tcl,Python, Ruby -- 或者是更高层次的编译语言比如 C/C++,或者是 Java。即使如此,你会发现,使用 shell 来原型开发你的应用,在开发步骤中也是非常有用的。

我们将开始使用 Bash,Bash 是"Bourne-Again shell"首字母的缩写,也是 Stephen Bourne 的经典 的 Bourne shell 的一个双关语。Bash 已经成为了 所有 UNIX 中 shell 脚本的事实上的标准了。同时这本书也覆盖了绝大部分的其他一些 shell 的原 则,比如 Korn Shell,Bash 从 ksh 中继承了一部分特性,2C Shell 和它的变种。(注意:C Shell 编程是不被推荐的,因为一些特定的内在问题,Tom Christiansen 在 1993 年 10 月指出了这个问题 请在这里看具体内容。)

接下来是脚本的一些说明。在展示 shell 不同的特征之前,它可以减轻一些阅读书中例子 的负担。本书中的例子脚本,都在尽可能的范围内进行了测试,并且其中的一些将使用在真 实的生活中。读者可以运行这些例子脚本(使用 scriptname.sh 或者 scriptname.bash 的形式), 3并给这些脚本执行权限(chmod u+rx scriptname),然后执行它们,看看发生了什么。如果存 档的脚本不可用,那么就从本书的 HTML,pdf 或者 text 的发行版本中把它们拷贝粘贴出来。考虑到 这些脚本中的内容在我们还没解释它之前就被列在这里,可能会影响读者的理解,这就需要读者 暂时忽略这些内容。

除非特别注明,本书作者编写了本书中的绝大部分例子脚本。


  1. 这些在 builtins 章节被引用,这些是 shell 的内部特征。 

  2. ksh88 的许多特性,甚至是一些 ksh93 的特性都被合并到 Bash 中了。 

  3. 根据惯例,用户编写的 Bourne shell 脚本应该在脚本的名字后边加上.sh 扩展名。 一些系统脚本,比如那些在/etc/rc.d 中的脚本,则不遵循这种命名习惯。