如果你经常使用正则表达式(regular expression),那么一定熟悉UNIX的两个文本工具:sed和awk。这两个工具很易学,而且都对模式匹配(patten match*译者注:这里的意思应该是给定若干的条件,对符合条件的对象进行预定的处理,也可以说是条件匹配*)很有用。Sed是流编辑器;它由命令simple ed得名;awk是一种编程语言,它的名字是它的三个开发者名字(Aho、Weinberger和Kernighan)的首字母组合得到的。我将在本文中介绍sed和awk的基本知识,进一步的知识将在以后的几篇文章中阐述。
Sed和awk都很胜任自动单调的(automating monotonous)文本编辑任务,该任务一般在文本编辑器用交互方式完成的。Sed和awk都是基于流的,也就是说它们的输入来自文本文件中——一次得到一行——然后产生标准输出。
Sed主要用于对单个或多个文件的重复编辑。Awk,做为一种编程语言,可以用来处理结构化的数据,然后产生格式化的报告。Sed和awk都可以像外壳脚本(shell script)那样被执行;每一个动作都是顺序运行的。Sed脚本通常用于简单任务,如完成条目(如从一个或者一系列文件中获取的方法的名字)的连接。Awk更适合完成复杂的任务,如重新格式化数据或建立定制报告(custom report)。
Awk是一个完整的编程语言,它的作用不限于sed那样的文本编辑器。Awk擅长从系统记录(system log)或来自数据库(该数据库基于文本)的数据来生成报告。然而,为了发挥awk的用处,数据必须是结构化的,这是awk本身所决定的。
表A
sed
|
awk
|
- Double/triple-space a file
- 转化DOS/UNIX 的新行(newline)
- 删除前后的空格
- 在所有/全部行上进行取代操作
- 删除连续的空行
- 删除文件开头和结尾的空行
|
- 管理小的、个人的数据库
- 产生报告
- 验证数据
- 生成下标、执行其它文档预备任务
- 试验算法,这些算法稍后可以由其它语言实现
- 处理UNIX命令的结果
- 更合理地处理命令行的参数
|
Sed和awk的常见用法
Sed与awk使用类似的语法,这就简化了对它们的学习过程。Sed程序由命令行组成,而awk程序是由编程语句和函数组成的。
Sed的运行过程
我们已经提到,sed脚本是顺序执行的。首先,模式匹配上了,程序就开始运行。然后,对输入文件的当前行运行脚本文件,每运行脚本文件的一行,都产生相应的输出。但脚本中的所有命令行都执行完了之后,sed开始对对输入文件的下一行开始执行脚本中的指令。
你可以在UNIX的命令行中这样调用sed:
#sed [选项] 脚本文件/命令输入文件
下面以一个记载联系方式的名为phonelist.txt文件为例,介绍sed的用法。记录中包括姓名、家庭电话、宿舍电话和E-mail地址:
John Doe, 100-555-1111, 100-555-1112, [email protected]
Jane Doe, 100-555-2222, 100-555-1113, [email protected]
Jimmy Dean, 101-555-1111, 101-555-2222, [email protected]
让我们首先看看比较简单的sed编辑命令:
$sed ‘s/100-/(100) /’ phonelist.txt
该命令将把区号为100的第一个电话号码从xxx-xxx-xxxx格式转换到(xxx) xxx-xxxx格式。
如果我们想对所有的区号为100的电话号码都做上述格式转换,就需要使用/g选项。
$sed ‘s/100-/(100) /g’ phonelist.txt
该操作作用于输入文件的所有的行。我们也可以在一个命令行上执行多个指令,这可以通过使用-e选项,也可以用分号(;)来分隔命令,如:
$sed –e ‘s/100-/(100) /g’-e ‘s/101-/(101) /g’ phonelist
$sed ‘s/100-/(100) /g’; ‘s/101-/(101) /g’ phonelist
(*译者注:注意要把输入文件的行和命令行区分开来,前者在本例中指得是文件phonelist.txt的某一行,如“John Doe, 100-555-1111, 100-555-1112, [email protected]”,后者指的是sed用户在敲入UNIX的命令行或者sed脚本文件中的某一行。*)
当需要使用多个命令时,用脚本文件也许更加合适。脚本文件的格式很简单——一行写一条命令:
$sed –f scriptfile inputfile
我们可以用外壳重定向(shell redirect)来把输出保存到文件中。
还有一个常用选项是-n,它专门用于限制命令行是否产生输出。使用-n选项后,只有末尾有“/p”选项的命令行才产生输出,如:
$sed –n ‘s/pattern/substitute/p’ inputfile
该命令将会只输出输入文件改变的部分。
Awk的运行过程
Awk也可以用命令行或者脚本文件的方式处理单个或者多个文件:
$awk ‘指令’ 输入文件
$awk –f 脚本文件输入文件
Awk将每一行分解为记录,用空格或者制表符来区分记录中的字段。它允许在任何过程中以任何方式访问这些字段。$0表示整个记录(行),$1、$2、……指向输入行的各个字段。
如果没有指定模式,则使用默认模式,即整个输入文件。如:
$awk ‘{ print $1}’ phonelist.txt
就会输出如下结果:(整个输入文件的$1字段都输出来了)
John
Jane
Jimmy
如果没有给定处理过程,就使用默认处理print(打印),如:
$awk ‘/Jane/’ phonelist.txt
将会输出:
Jane Doe, 100-555-2222, 100-555-1113, [email protected]
正式用法是同时指定匹配模式和处理过程。让我们用下面这条命令来输出Jane的家庭电话号码:
$awk ‘/jane/ { print $3}’ phonelist
这会输出:
100-555-2222
我们可以通过用-F选项来选定任何我们喜欢的间隔符。因为我们的输入文件phonelist是用逗号来分隔的,所以我们需要用逗号来做分隔符。
命令如下:
$awk –F, ‘{print $1}’ phonelist.txt
这条命令将会输出下列名字:
John Doe
Jane Doe
Jimmy Dean
如果我们想格式化输出姓氏和名字,我们可以这么做:
$awk –F, ‘{print $2 $1}’ phonelist.txt
结果是:
Doe, John
Doe, Jane
Dean, Jimmy
为了达到一行只输出一个字段的目的,我们可以用分号去分开打印语句,如:
$awk -F, '{ print $1; print $2; print $3; print $4 }' phonelist.txt
结果就会这样:
John Doe
100-555-1111
100-555-1112
[email protected]
Jane Doe
100-555-2222
100-555-1113
[email protected]
Jimmy Dean
101-555-1111
101-555-2222
[email protected]
现在总结一下,sed主要用来改变数据;awk用来重新排列数据。通过sed和awk的高级编程可以采集到大量的信息。