序
很多人把大把的时间花在一些简单、机械的数据处理上——改改数据格式、看看是否规整、找找特定的条目、加总数字、打印报表,诸如此类的小事。这些重复枯燥的琐事本是程序语言的强项,但真要用C、Pascal这样的标准编程语言去解决却也有苦难言。
你需要一种能在寥寥数行之内解决这类问题的编程语言,awk就有此奇功。用awk编程就像在写规则说明:问,遇到这样的数据怎么办?答,如此这般。这“问”即为欲处理数据之匹配模式;这“答”即是找到所需数据后,awk采取的动作。awk每读入一行就会逐项查找那些模式,一旦匹配,执行其后的动作。模式既可以是正则表达式,也可以是用于字串、数字、域(field)、变量、序列元素(array element)间比较的关系运算符(comparison operation)。awk对动作没有太多的限制。描述动作的语言有点像C,但是不用声明,字串也数字也是内置的数据类型。
awk会自动扫描输入文件并将之拆分成域。很多事情,比如拆分输入、存储管理、初始数据,都由awk代劳。这也是awk写出的程序更短小的原因。awk很适合来做这些数据处理。写上一两行awk程序,执行,解决问题,然后丢弃。实际上,awk作为通用的编程工具,完全可以取代某些专用程序。
awk很适合来开发程序原型,因为它表达简洁、操作方便。从很少的几行代码开始,不断修改直至达到目标,同时还可以尝试不同的设计。因为程序短小,没有什么负担,即便设计方向错了也容易扭正。而且只要设计正确,用其他语言来改写awk程序也很简单。
本书的框架
本书的初衷是介绍awk以及如何高效的使用awk。第一章(An AWK Tutorial)告诉你如何起步。内容很简单,读过几页你就可以开始写一些能用得上的程序了。这章的例子很短,主要是与awk交互。
第二章(The AWK Language)开始系统地介绍整个awk语言。比起第一章,这一章的例子就多了,而且开始变得又长又臭_,就像读说明书一样。所以,第一次读的时候你可能会跳过去。
本书余下的部分还收录了很多示例,从中你不仅可以看到awk应用之广泛,还可以了解其精髓。之所以选择这些示例,有些是很正式的用法,有些只是帮助理解,还有些仅仅是因为好玩。
第三章(Data Processing)着重介绍了检索、变换、化简和数据验证,这也是设计awk的初衷。本章的最后,还讨论了如何处理大数据(比如地址簿)。
awk很适合管理个人的小型数据库。第四章(Reports and Databases)讨论了如何从数据库生成报表、如何搭建简易的关系型数据库(relational database system)、如何用查询语言(query language)在多个文件中查找。
awk处理文本就像其他语言操作数字一样得心应手。文本处理是awk的专长。第五章(Processing Words)描述了如何生成文本,这在文档预处理中用处不小。我们还把写本书时用到的索引程序也收录进来了。
第六章(Little Languages)是关于“袖珍语言”,也就是专门用于某个狭窄领域的特定语言。用awk来写一些小型的转换程序很方便,因为它有很多在词法或是表管理(table-management)层面上支持转换的运算符。在这一章会看到一个(awk写的)汇编程序、一门图像语言和几个计算器。
awk中变量不用声明,存储管理也很容易,所以很适合表述某些算法。awk写出的程序和伪代码很相似,更重要的是它还可以运行。第七章(Experiments with Algorithms)着重探讨了如何用awk来考察算法,从测试到执行效率(,我们都有涉及)。我们用awk实现了几个排序算法,以及一个Unix版的make程序。
第八章(Epilog)记叙了awk之所以为awk的历史原因,并就性能和扩展给出了建议。
附录一(A AWK Summary)小结了这门语言;附录二(Answers to Selected Exercises)给出了部分习题答案。
你可以从从头开始读起,试试运行你自己遇到的小程序。快速扫过第二章,重点看看小结和表(table),不要急于陷入细节之中。后面的章节几乎相互独立,不必在意顺序,完全可以凭兴趣决定。
书中的示例
对本书的示例,收录时我们有几个考虑。第一,当然是展示怎么用好awk。我们想尽可能地涵盖awk的十八般武器,尤其钟爱关联序列(associative array)和正则表达式类具有awk特色(译注:awk较早引入了关联序列的概念,待考证)的工具。
第二个考虑是展现awk的广泛使用。从数据查询到电路设计,从数值分析到图像处理,从程序编译到系统管理,从非程序员的启蒙语言到软件工程的课程实践,随处可见awk的身影。我们希望书中罗列的各式应用能使你们受到更多的启发。
第三个考虑是为了说明计算操作的无处不在。这本书选取的例子涉及很广,有关系型数据库、简易的汇编程序和解释程序、画图语言、一个描述awk子集的递归下降解析程序、基于make的文件更新程序等等。但所有的例子都试图用一小段awk程序,以便于理解的方式,去揭示事物运作的本质。
此外,我们还尽可能地介绍一些设计程序的方法。比如awk支持得很好的快速原型方法,以及不那么直观的分而治之策略:把一个大工程分割成一堆小问题,每一个都只着眼问题的某一方面。还有开发写程序的程序,即领域语言。定义好用户接口,并提出合理的实现。虽然这些思想都是用awk来体现的,但它们普遍适用,值得每个程序员重视。
书中所有的例子都可以运行。我们曾想保证程序不含错误,但与其把精力放在怎么处理所有的非法输入上,或者为了确保正确而放弃功能扩展,还不如集中精力好好琢磨怎么把事理说透。
AWK的演化
1977年,笔者想试试Unix下的grep和sed能不能像处理数字那样处理文本,所以设计实现了最初的awk。这也得益于我们对正则表达式和可编程编辑器(programmable editor)的兴趣。虽然我们把awk定位于解决日常任务,但它很快被用户用到大项目之中。有些大项目需要一些设计之外的特性,所以1985年我们在最初版本的基础上开发了一个更强大的版本。
最重要的新特性是用户可以自定义函数了。另外还增强了动态正则表达式(dynamic regular expression)、文本替换和模式匹配函数;增加了一些内置函数和内置变量;添加了一些新的操作符和语句;支持多文件输入和命令行参数。错误管理也有所改进。第一章我只会用到原始版本的特性;后面章节的例子会涉及到一些新特性。
我们用的这个awk版本是Unix System V Release 3.1。这个版本的awk源代码可以从AT&T下的Unix工具软件发布系统上获得。⋯⋯
因为awk是在Unix下开发的,它的一些特性只在Unix下才能施展,有些我们的例子中也会提到。另外,有些Unix工具,比如sort,只有Unix版本。抛开这些限制,awk在大部分环境中都应用得很不错,包括MS-DOS。
当然,awk并不完美。它不够规整(“it has its share of irregularities, omissions, and just plain bad ideas”这句真不会翻),甚至有时还慢得令人发指。但同时它也能出色地满足各种需求。awk给了我们很大帮助,希望你也能同样受惠于它。
致谢
我们深深地受惠于那些对本书的草稿给出过评论和建议的朋友们。这里,我们要特别感谢Jon Bentley(译注:《编程珠玑》那位神作者?),他的激情多年来一直在赋予我们灵感。Jon贡献了很多awk使用、教学方面的想法、经验,甚至是代码。他非常仔细地审阅了多部手稿。还有Doug McIlroy,他无与伦比的天才极大地影响了我们,使我们从框架到内容都进行了很多修改。还有很多朋友对手稿给出了极富建设意义的评论,Susan Aho、Jaap Akkerhuis、Lorinda Cherry、Chris Fraser、Eric Grosse、Riccardo Gusella、Bob Herbst、Mark Kernighan、John Linderman、Bob Martin(译注:难道是Martin大神?)、Howard Moscovitz、Gerard Schmitt、Don Swartwout、Howard Trickey、Peter van Eijk、Chris Van Wyk、Mihalis Yannakakis。谢谢你们!
Alfred V. Aho
Brian W. Kernighan
Peter J. Weinberger