Software Design 中文版 03第1章:从UNIX文本处理的基础开始 sed和AWK超级入门_Software Design 中文版 03第1章:从UNIX文本处理的基础开始 sed和AWK超级入门试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > Software Design 中文版 03 > 第1章:从UNIX文本处理的基础开始 sed和AWK超级入门

Software Design 中文版 03——第1章:从UNIX文本处理的基础开始 sed和AWK超级入门

在正式学习sed 和AWK 之前,本章将先介绍UNIX 文本数据的处理方法。了解了这些,就能回答“为什么现在有必要学习sed、AWK”这个问题了。 UNIX 的基础 20 世纪60 年代后期,有一个名为Multics、在当时具有高性能(所谓的高性能实际上只是很复杂而已)的实验性OS项目。虽然这个项目最终失败了,但吸取这个项目的经验教训之后,便诞生了简洁型OS。这种OS与复杂的Multics 正相反,当初被称作Unics(后来改名为UNIX)。 正如其名,这种OS非常简单,而且附带的工具集也都是单性能的、简单的。特别是因为最初的使用者大都是程序员或计算机研究人员,他们为了得到自己想要的结果,会把单性能的命令组合起来使用,因此倒也不觉得吃力。像这样,为了把多个命令组合起来使用,人们进一步发展了在OS设计之初就有的标准输入输出的想法,从而出现了作为命令组合手段的“管道”结构。 标准输入输出标准输入输出是UNIX 设计之初就引进的输入输出的通道。是一种在程序被执行时,OS可以自动地分配输入、输出以及错误输出设备的机制。 在UNIX以前的计算机系统中,在程序使用输入输出设备时,需要使用JCL等,用程序来分配输入输出设备,非常麻烦。而在UNIX中,因为输入输出都事先在OS上进行了分配,即使有多个程序在同时使用,使用者也好程序也好,并不需要什么特别的操作就可以使用输入输出设备了。 OS在程序启动时会执行一个叫作startuproutine 的共通的初始化处理。在这个处理中准备好标准输入输出以便于使用。标准状态的分配是这样的:输入设备(标准输入)是键盘,输出设备(标准输出)以及错误输出设备(标准错误输出)是终端。 shell 和重定向 在UNIX中,内核和用户接口通常是由叫作shell 的程序来提供的。登录UNIX 系统时,指定的登录shell 将被执行,终端表示提示符,等待用户输入。用户输入命令,系统就会解释输入的命令字符串,生成进程,把命令的执行结果显示出来。shell 承担的责任非常重大。 在shell 上执行程序也可以利用标准输入输出。不过,如果使用shell 上的重定向功能,就可以简单地变更标准输入输出。比如使用符号“>”,就是指示变更标准输出的输出位置。标准状态的标准输出是分配给终端的,命令的执行结果也会在终端表示出来,如果把输出位置重定向到文件,执行结果就会保存到文件中(图1)。如果在输出位置文件已经存在了,那么将会先删除文件的所有内容,再把新的内容写到文件中去。 如果使用符号“>>”则表示这样的指示:在变更标准输出的输出位置的同时,(输出位置文件已存在的情况下)追加文件内容(图2)。 同样,使用符号“<”可以重定向标准输入的输入方。从标准输入接收数据的命令通常是从键盘接收,但如果把输入方重定向为文件,就可以从文件读取数据了(图3)。 可以同时重定向输入和输出。此时,命令的输入和输出都可以分配给文件等(图4)。另外,输入和输出可以指定为同一文件。 但是在这种情况下执行命令可能得不到所期望的结果。要注意命令会把输入文件(=输出文件)内容清空(图5)。 管道和流 管道是输入输出的想法进一步发展的结果,是为了实现某个命令的标准输出直接作为另一个命令的输入而提供的单向数据流机制。标准的shell 一般用符号“|”来表示管道。比如commandA|commandB就表示A的标准输出作为B的标准输入来使用。在管道中流通的数据称为“流”,是单纯的字节流,并没有像“行”这样的数据分隔概念。 空管道读入(从管道的输入侧输入数据),直到能有数据读出为止,读入处理都是加锁的。另外,由于输出侧的命令不能从管道读入数据等原因,OS在内部为管道预留的缓存都满了的情况下,(从管道输出侧读入数据直到缓存为空为止)对管道的写入操作也是加锁的。 像这样,利用管道把多个命令组合起来使用时,为了可以让多个命令都流畅地使用数据,一般只使用由ASCII 码构成的文本数据。为了在发生错误时也不阻碍管道中的数据流,错误信息不输出到标准输出,而是输出到标准错误输出。 对标准输入中输入的数据进行加工,并在标准输出中进行输出的命令叫“过滤器”命令。这次要介绍的sed 和awk 也是为了加工数据流而做成的文本加工过滤器命令。 sed 和awk sed 和awk 的基本功能都是对标准输入中输入的文本数据进行加工,在标准输出中进行输出。那么为什么会有多个具有相同功能的命令存在呢? sed 首先来看一下sed,它的名字由StreamEditor 而来。正如其名,设计它是用来作为加工文本流数据编辑器的。所以,sed 基本以行为单位来处理输入数据。比如它就在对输入行用正则表达式进行匹配替换等方面发挥了作用。我们也可以使用分支以及一种叫作patternspace 的模式空间,不过用法有些难懂,实际中也不太会用到(笔者认为)。 在文件/etc/passwd 中,将/bin/bash 指定的登录shell 全部替换成/usr/local/bin/bash 的例子如图6 所示。cat 的输出通过管道传给sed 进行替换处理A。/etc/passwd 中表示登录的shell 是最后一项,所以将以/bin/bash 结束的行用sed 的s 命令替换成/usr/local/bin/bash。最后的$表示作为替换对象的/bin/bash 处于行尾,所以即使在行中间出现/bin/bash,也不会被当作替换对象。在这个例子中,因为替换对象的字符串中含有“/”,所以没有采用sed 的标准记述法“/”,而是用“!”进行记述。 A 本来可以对sed 直接指定文件,为了体现使用管道才和cat 一起使用的。 awk 反之,awk 会事先把输入的数据根据字段单位进行分割。在没指定分割单位的情况下,以输入数据中的空格或Tab 为分隔符对数据进行分割,因为对单位数据的加工比较容易一些。 而且,与sed 相比,它以更接近编程语言的文法记述处理,此外还包含了通过正则表达式进行的字符串操作、简单的数学函数功能等。假设编程语言的基本功能只有分支、循环、变量的使用,而这些awk都可以实现,那么它完全可以称为编程语言。事实上,笔者已经只靠使用awk的功能做了一个简单的语法解析工具。 使用和刚才的sed 例子同样的/etc/passwd文件,统计各个登录shell 的用户数的例子如图7 所示。BEGIN节记述的是在开始读入输入行之前要执行的处理。/etc/passwd 的字段分隔符是:,所以用FS=”:”把字段分隔符改为:。此后的主要处理部分记述的是对于所有的输入行重复执行以下处理。awk用变量NF来保存输入行的字段数,$NF可以用来访问最后一个字段的值。在这个例子中,因为最后一个字段保存的是登录shell,把登录shell 作为索引的关联数组shells 的值加1。END节记述的是在所有的输入行读入完成之后要执行的处理。这个例子是输出作为统计结果的关联数组shells 的内容。 综上所述,sed 和awk 各有擅长之处。应该根据处理内容和使用者的喜好灵活运用。 正则表达式 不管是对于sed 还是awk,正则表达式都很重要。正则表达式是指用通用的模式表示法来表示字符串排列的手法之一。在表示字符排列时,通过使用一种叫作元字符的特殊符号表示的模式,正则表达式可以灵活地对应并不完全一致的字符排列。UNIX在刚开始时的各种命令都活用了正则表达式。 正则表达式所使用的元字符有*、^、$、[和]等,各代表着特殊的含义(表1)。正则表达式基于非常高深的理论,内容很深奥,要详细解说起来,需要用一整本书的篇幅来写,所以这里只做简单的说明。通过使用如表1 那样的元字符记述模式,可以灵活地匹配字符串。 正则表达式“.*”可以匹配包括空字符串的所有字符串 正则表达式“https?”可以匹配http 或https “ ^abc”匹配以abc 开头的字符串,“xyz$”匹配以xyz 结尾的字符串 foo[135]与foo1、foo3、foo5中任一个匹配 表1 几种具有代表性的正则表达式的元字符 其他命令 除了sed 和awk 以外,使用正则表达式的命令还有很多。虽然难度有些高,但是如果可以将正则表达式的用法运用自如,对使用者而言,UNIX的世界将会变得很宽广。 比如,我们经常使用的grep 命令也是正则表达式的活用。grep 这个词原本就是表示一个叫作ed 的编辑器的命令文法,它表示把g(Global = 把整个文件作为对象)用re(RegularExpression = 正则表达式)p(Print = 表示)出来。 使用和sed、awk 的例子一样的文件/etc/passwd,把含有bash 的行用ed 命令表示出来的例子如图8 所示。执行ed 编辑器的命令g/bash/p,显示整个文件中含有bash 正则表达式的行。ed 是编辑器,所以通常是用于对话,这里是单纯作为UNIX命令,它也可以进行从标准输入接收命令的类似于过滤器的操作。从标准输入接收编辑器命令g/bash/p然后执行。 为什么编辑器的命令要制作成独立的命令呢?因为这些是非常便利的有意义的命令。从文件中抽取出和模式匹配的行,这是计算机在各种作业时常用的处理。“没有grep 的环境就不能工作”,也许使用UNIX的人都会这么想吧。如此便利的通用性高的命令,为了能使其方便使用,就把它做成了独立的命令,并一直留用至今。这也恰好和刚开始提到的“简单的单性能的命令”的想法相吻合。 使用和图8 的例子相同的文件/etc/passwd,用grep 命令表示含有bash 的行的例子如图9 所示。其实没有什么好说明的,就是显示整个文件中含有bash 正则表达式的行。比使用ed 命令完成相同的功能显得更简单。 类似的命令还有很多。比如tr 命令是替换输入字符串的命令,这个命令也可以说是sed的子集。这个命令就是把sed 功能中使用最多的字符串替换功能作为“简单的单性能的命令”抽取出来了。在UNIX中有多个像这样把已有命令的部分功能抽出来做成专用命令的例子。 总 结 标准输入输出、管道、正则表达式,UNIX的一些特色功能都简略地介绍完了。篇幅所限,我们不可能分别对这些功能进行详细的说明。如果此文能唤起大家对UNIX提供的便利强大的功能的兴趣,笔者将感到非常荣幸。 UNIX 和字符编码 UNIX 本来就是在美国开发的OS,所以1 字节当然就是作为1 个字符来处理的。但是,像日文、中文这样有汉字的,可能会有几千甚至几万个文字,用1 个字节是不可能表示所有的文字的。因此就需要在用两个或多个字节来表示1 个字符的字符编码上下功夫了。 最初在UNIX 中使用最多的是一种叫作EUC(Extended UNIX Code)的字符编码。日语的平假名、片假名、代表性的汉字用两个字节表示,英文数字和日语也很容易进行区别,特别是多用于在UNIX 环境中表示日语的情况。最近兴起了一种叫作UTF-8的字符编码,Mac OS X 还有一部分Linux 发行版都采用UTF-8 作为标准字符编码。 以前,在UNIX 环境中使用诸如日语这样的多字节字符是非常困难的,甚至有些命令根本就不支持多字节字符。不过,现在通过给LANG 环境变量设置适当的值,通知系统和命令可以使用的字符编码,几乎所有的命令都可以正确地处理多字节字符。比如,在日语环境中字符编码采用UTF-8,只要把LANG指定为ja_UP.UTF-8,那么几乎所有的命令都可以正确地处理多字节字符。本特辑中提到的sed 和awk 当然也是可以正确地处理多字节字符的。 延伸阅读 Linux 系统架构和应用技巧 清华计算机系副系主任陈文光、北航计算机学院院长吕卫锋作序推荐本书内容涉及Linux 内部结构、虚拟化基础设施环境的构建、内核源代码的阅读以及RHEL6新功能综述。通过搭建虚拟化基础设施,给读者提供了方便实用的Linux 系统的学习和实践环境;同时,设计了10 个可操作的脚本实验,尽可能覆盖Linux 操作系统的关键应用技术,包括进程监控、远程登录、文本处理等。其中的技巧根植于作者的多年经验,具有极强的现场感和可操作性。 正则表达式必知必会(修订版) 全球技术人员正则表达式入门首选,紧贴实战需求正则表达式是一种威力无比强大的武器,几乎在所有的程序设计语言里和计算机平台上都可以用它来完成各种复杂的文本处理工作。本书从简单的文本匹配开始,循序渐进地介绍了很多复杂内容,其中包括回溯引用、条件性求值和前后查找等。每章都为读者准备了许多简明又实用的示例,有助于全面、系统、快速掌握正则表达式,并运用它们去解决实际问题。

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《Software Design 中文版 03》其他试读目录

• 第1章:从UNIX文本处理的基础开始 sed和AWK超级入门 [当前]
• 第2章:简单强大的文本处理工具