3.10. sed 和 awk 联合使用

未匹配的标注

sed 和 awk 联合使用

在 UNIX 里面,管道是来将一个程序的输出传给下一个程序的输入。让我们看一些结合 sed 和 awk 来产生报告的例子。 用一个州的全名来替换州的邮政缩写的 sed 脚本是足够通用的,可以作为一个名为 nameState 的脚本再次使用:

➜  ch02 git:(daily) ✗ cat nameState
s/ CA/, California/
s/ MA/, Massachusetts/
s/ OK/, Oklahoma/
s/ PA/, Pennsylvania/
s/ VA/, Virginia/

当然,你可能想处理所有的州,而不只是这五个。如果你在不是邮件列表的其他文档上运行这个脚本,你需要确定它不会造成不需要的替换。

这个程序——使用输入文件 list——的输出和我们已经看到的是一样的。在下个例子里,被 nameState 产生的输出被传到一个从每个记录里面提取州名的 awk 程序。

➜  ch02 git:(daily) ✗ sed -f nameState list | awk -F, '{print $4}'
 Massachusetts
 Virginia
 Oklahoma
 Pennsylvania
 Massachusetts
 Virginia
 California
 Massachusetts

这个 awk 程序处理 sed 脚本产生的输出。记住 sed 脚本用一个逗号和州的全名替换了缩写。实际上它将包含城市和州的第三个字段分成两个字段, “$4” 引用第四个字段。

我们现在做的这些可以完全用 sed 来完成,但是可能更复杂,通用性也更低。而且,因为 awk 允许你替换匹配的字符串,你完全可以用一个 awk 脚本来得到这个结果。

练习:只用 awk 脚本得到上面例子的结果?

普通替换

创建一个 awk 脚本文件,内容如下:

/ CA/{sub(/ CA/, ", California"); print $4}  
/ MA/{sub(/ MA/, ", Massachusetts"); print $4}
/ OK/{sub(/ OK/, ", Oklahoma"); print $4}
/ PA/{sub(/ PA/, ", Pennsylvania"); print $4}
/ VA/{sub(/ VA/, ", Virginia"); print $4}

命令行运行:

➜  ch02 git:(daily) ✗ awk -F, -f nameState-awk list
 Massachusetts
 Virginia
 Oklahoma
 Pennsylvania
 Massachusetts
 Virginia
 California
 Massachusetts

这里使用了 awk 的 sub 替换函数。

循环替换

待后面学习了循环和数组后,再来增加一个循环和数组的解法。

虽然这个程序的结果不是很有用,但它可以被传递给 sort | uniq -c,这样的话就可以将这些州按字母顺序排列成一个列表,并对每一个州出现次数进行计数。

➜  ch02 git:(daily) ✗ awk -F, -f nameState-awk list | sort | uniq -c
   1  California
   3  Massachusetts
   1  Oklahoma
   1  Pennsylvania
   2  Virginia

将多个命令放入 shell 脚本

现在我们要做更有趣的事情。我们想生成一个报告,它按州的名称进行排序,然后列出州的名称和居住在这个州里面的每个人的姓名。下面的例子显示了 byState 程序。

awk -F, '{ 
    print $4 ", " $0 
    }' | 
sort |
awk -F, '
$1 == LastState { 
    print "\t" $2 
}
$1 != LastState { 
    LastState = $1
    print $1 
    print "\t" $2
}'

这个 shell 脚本有三个部分。程序调用 awk 来产生 sort 程序的输入,然后再一次调用 awk 来测试排序后的输入,并决定当前记录中的州名与前一个记录中的是否相同。让我们来看一下这个脚本的实际效果:

➜  ch02 git:(daily) ✗ sed -f nameState list | ./byState
 California
     Amy Wilde
 Massachusetts
     Eric Adams
     John Daggett
     Sal Carpenter
 Oklahoma
     Orville Thomas
 Pennsylvania
     Terry Kalkas
 Virginia
     Alice Ford
     Hubert Sims

名称按州排序。这是使用 awk 从结构化数据产生报告的一个典型例子。要检查 byState 程序是如何工作的,让我们独立地来看每一部分。它被设计为从 byState 这个程序读取输入,然后期待 “$4” 成为州名。看一下这个程序的第一行产生的输出:

➜  ch02 git:(daily) ✗ sed -f nameState list | awk -F, '{print $4 ", " $0}'
 Massachusetts, John Daggett, 341 King Road, Plymouth, Massachusetts
 Virginia, Alice Ford, 22 East Broadway, Richmond, Virginia
 Oklahoma, Orville Thomas, 11345 Oak Bridge Road, Tulsa, Oklahoma
 Pennsylvania, Terry Kalkas, 402 Lans Road, Beaver Falls, Pennsylvania
 Massachusetts, Eric Adams, 20 Post Road, Sudbury, Massachusetts
 Virginia, Hubert Sims, 328A Brook Road, Roanoke, Virginia
 California, Amy Wilde, 334 Bayshore Pkwy, Mountain View, California
 Massachusetts, Sal Carpenter, 73 6th Street, Boston, Massachusetts

这个 sort 程序默认是按字母顺序对行排序的——从左到右查看字母。为了按州而不是按人的名字对记录排序。我们在记录的前面插入州作为排序关键字。现在 sort 程序能帮我们排序了。(注意使用 sort 实用程序让我们不用在 awk 里面写排序的例行程序。)

第二次调用 awk 时,我们执行了一个编程任务。这个脚本查看每个记录的第一个字段(州)来决定它是不是和上一个记录相同。如果不同,打印出州的名称和这个人的名字。如果是相同的,就只打印这个人的名字。

$1 == LastState { 
    print "\t" $2 
}
$1 != LastState { 
    LastState = $1
    print $1 
    print "\t" $2
}

这里有几个重要的地方,包括对变量赋值,测试每一个输入行的第一个字段来看它是否包含一个变量字符串,然后打印一个 tab 来排列输出数据。注意,我们在使用一个变量前不需要先对它赋值(因为 awk 的变量被初始化为空字符串)。这是一个小的脚本。但是你将在第 12 章 《全面功能的应用程序》中看到用来在一个大得多的索引程序中比较索引的同种例行程序。然而目前不需要过于担心去理解每一个语句是做什么的。我们这里的目的是向你概述 sed 和 awk 的功能。

小结

在本章中,我们学习了 sed 和 awk 的基本操作。我们学习了重要的命令行选项,并向你介绍了脚本。在下一章中,我们将学习正则表达式,这两个程序都用它来匹配输入中的模式。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~