zz 通用线程:Awk 实例,第 2部分
2011-11-27 18:43
267 查看
通用线程:Awk 实例,第 2部分
记录、循环和数组Daniel Robbins (drobbins@gentoo.org), 总裁兼 CEO, Gentoo Technologies, Inc.
简介: 在这篇 awk简介的续集中,Daniel Robbins 继续探索awk(一种很棒但有怪异名称的语言)。Daniel将演示如何处理多行记录、使用循环结构,以及创建并使用 awk数组。阅读完本文后,您将精通许多 awk的功能,而且可以编写您自己的功能强大的 awk 脚本。
标记本文!
发布日期: 2001 年 1 月 01 日
级别: 初级
访问情况 : 6703 次浏览
评论: 1 (查看 | 添加评论 - 登录)
![](http://dw1.s81c.com/developerworks/i/stars120x20-4b.jpg)
平均分 (13个评分)
为本文评分
多行记录
awk 是一种用于读取和处理结构化数据(如系统的 /etc/passwd 文件)的极佳工具。/etc/passwd 是 UNIX 用户数据库,并且是用冒号定界的文本文件,它包含许多重要信息,包括所有现有用户帐户和用户标识,以及其它信息。在我的 前一篇文章 中,我演示了 awk 如何轻松地分析这个文件。我们只须将 FS(字段分隔符)变量设置成 ":"。
正确设置了 FS 变量之后,就可以将 awk 配置成分析几乎任何类型的结构化数据,只要这些数据是每行一个记录。然而,如果要分析占据多行的记录,仅仅依靠设置 FS 是不够的。在这些情况下,我们还需要修改 RS 记录分隔符变量。RS 变量告诉 awk 当前记录什么时候结束,新记录什么时候开始。
譬如,让我们讨论一下如何完成处理“联邦证人保护计划”所涉及人员的地址列表的任务:
Jimmy the Weasel 100 Pleasant Drive San Francisco, CA 12345 Big Tony 200 Incognito Ave. Suburbia, WA 67890 |
BEGIN { FS="\n" RS="" } |
address.awk
BEGIN { FS="\n" RS="" }{ |
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345 Big Tony, 200 Incognito Ave., Suburbia, WA 67890 |
OFS 和 ORS
在 address.awk 的 print 语句中,可以看到 awk 会连接(合并)一行中彼此相邻的字符串。我们使用此功能在同一行上的三个字段之间插入一个逗号和空格 (", ")。这个方法虽然有用,但比较难看。与其在字段间插入 ", " 字符串,倒不如让通过设置一个特殊 awk 变量 OFS,让 awk 完成这件事。请参考下面这个代码片断。
print "Hello", "there", "Jim!" |
Hello there Jim! |
address.awk 的修订版
BEGIN { FS="\n" RS="" OFS=", " } { print $1, $2, $3 } |
回页首
将多行转换成用 tab 分隔的格式
假设我们编写了一个脚本,它将地址列表转换成每个记录一行,且用 tab 定界的格式,以便导入电子表格。使用稍加修改的 address.awk 之后,就可以清楚地看到这个程序只适合于三行的地址。如果 awk 遇到以下地址,将丢掉第四行,并且不打印该行:
Cousin Vinnie Vinnie's Auto Shop 300 City Alley Sosueme, OR 76543 |
适合具有任意多字段的地址的 address.awk 版本
BEGIN { FS="\n" RS="" ORS="" } { x=1 while ( x<NF ) { print $x "\t" x++ } print $NF "\n" } |
print "\n"。
在主代码块中,创建了一个变量 x 来存储正在处理的当前字段的编号。起初,它被设置成 1。然后,我们使用 while 循环(一种 awk 循环结构,等同于 C 语言中的 while 循环),对于所有记录(最后一个记录除外)重复打印记录和 tab 字符。最后,打印最后一个记录和换行;此外,由于将 ORS 设置成 "",print 将不输出换行。程序输出如下,这正是我们所期望的:
我们想要的输出。不算漂亮,但用 tab 定界,以便于导入电子表格
Jimmy the Weasel 100 Pleasant Drive San Francisco, CA 12345 Big Tony 200 Incognito Ave. Suburbia, WA 67890 Cousin Vinnie Vinnie's Auto Shop 300 City Alley Sosueme, OR 76543 |
循环结构
我们已经看到了 awk 的 while 循环结构,它等同于相应的 C 语言 while 循环。awk 还有 "do...while" 循环,它在代码块结尾处对条件求值,而不象标准 while 循环那样在开始处求值。它类似于其它语言中的 "repeat...until" 循环。以下是一个示例:
do...while 示例
{ count=1 do { print "I get printed at least once no matter what" } while ( count != 1 ) } |
for 循环
awk 允许创建 for 循环,它就象 while 循环,也等同于 C 语言的 for 循环:
for ( initial assignment; comparison; increment ) { code block } |
for ( x = 1; x <= 4; x++ ) { print "iteration",x } |
iteration 1 iteration 2 iteration 3 iteration 4 |
break 和 continue
此外,如同 C 语言一样,awk 提供了 break 和 continue 语句。使用这些语句可以更好地控制 awk 的循环结构。以下是迫切需要 break 语句的代码片断:
while 死循环
while (1) { print "forever and ever..." } |
1永远代表是真,这个 while 循环将永远运行下去。以下是一个只执行十次的循环:
break 语句示例
x=1 while(1) { print "iteration",x if ( x == 10 ) { break } x++ } |
continue 语句补充了 break,其作用如下:
x=1 while (1) { if ( x == 4 ) { x++ continue } print "iteration",x if ( x > 20 ) { break } x++ } |
for ( x=1; x<=21; x++ ) { if ( x == 4 ) { continue } print "iteration",x } |
x,因为 for 循环会自动增加
x。
回页首
数组
如果您知道 awk 可以使用数组,您一定会感到高兴。然而,在 awk 中,数组下标通常从 1 开始,而不是 0:
myarray[1]="jim" myarray[2]=456 |
myarray,并将元素
myarray[1]设置成 "jim"。执行了第二个赋值语句后,数组就有两个元素了。
数组迭代
定义之后,awk 有一个便利的机制来迭代数组元素,如下所示:
for ( x in myarray ) { print myarray[x] } |
myarray中的每一个元素。当对于 for 使用这种特殊的 "in" 形式时,awk 将
myarray的每个现有下标依次赋值给
x(循环控制变量),每次赋值以后都循环一次循环代码。虽然这是一个非常方便的 awk 功能,但它有一个缺点 -- 当 awk 在数组下标之间轮转时,它不会依照任何特定的顺序。那就意味着我们不能知道以上代码的输出是:
jim 456 |
456 jim |
回页首
数组下标字符串化
在我的 前一篇文章 中,我演示了 awk 实际上以字符串格式来存储数字值。虽然 awk 要执行必要的转换来完成这项工作,但它却可以使用某些看起来很奇怪的代码:
a="1" b="2" c=a+b+3 |
c等于
6。由于 awk 是“字符串化”的,添加字符串 "1" 和 "2" 在功能上并不比添加数字 1 和 2 难。这两种情况下,awk 都可以成功执行运算。awk 的“字符串化”性质非常可爱 -- 您可能想要知道如果使用数组的字符串下标会发生什么情况。例如,使用以下代码:
myarr["1"]="Mr. Whipple" print myarr["1"] |
myarr["1"]="Mr. Whipple" print myarr[1] |
myarr["1"]和
myarr[1]看作数组的两个独立元素,还是它们是指同一个元素?答案是它们指的是同一个元素,awk 将打印 "Mr. Whipple",如同第一个代码片断一样。虽然看上去可能有点怪,但 awk 在幕后却一直使用数组的字符串下标!
了解了这个奇怪的真相之后,我们中的一些人可能想要执行类似于以下的古怪代码:
myarr["name"]="Mr. Whipple" print myarr["name"] |
myarr["name"],那么我们就在使用 关联数组 。从技术上讲,如果我们使用字符串下标,awk 的后台操作并没有什么不同(因为即便使用“整数”下标,awk 还是会将它看作是字符串)。但是,应该将它们称作 关联数组 -- 它听起来很酷,而且会给您的上司留下印象。字符串化下标是我们的小秘密。;)
回页首
数组工具
谈到数组时,awk 给予我们许多灵活性。可以使用字符串下标,而且不需要连续的数字序列下标(例如,可以定义
myarr[1]和
myarr[1000],但不定义其它所有元素)。虽然这些都很有用,但在某些情况下,会产生混淆。幸好,awk 提供了一些实用功能有助于使数组变得更易于管理。
首先,可以删除数组元素。如果想要删除数组
fooarray的元素
1,输入:
delete fooarray[1] |
if ( 1 in fooarray ) { print "Ayep! It's there." } else { print "Nope! Can't find it." } |
下一篇
本文中,我们已经讨论了许多基础知识。下一篇中,我将演示如何使用 awk 的数学运算和字符串函数,以及如何创建您自己的函数,使您完全掌握 awk 知识。我还将指导您创建支票簿结算程序。那时,我会鼓励您编写自己的 awk 程序。请查阅以下参考资料。
参考资料
您可以参阅本文在 developerWorks 全球站点上的 英文原文.
请阅读 developerWorks上的 awk 实例,第 1 部分。
如果想看好的老式书籍,O'Reilly 的 sed & awk, 2ndEdition是极佳选择。
请参考 comp.lang.awkFAQ 。它还包含许多附加 awk 链接。
Patrick Hartigan 的 awk tutorial 还包括了实用的 awk 脚本。
Thompson's TAWKCompiler 将 awk 脚本编译成快速二进制可执行文件。可用版本有 Windows 版、OS/2 版、DOS 版和 UNIX 版。
The GNUAwk User's Guide可用于在线参考。
关于作者
![](http://www.ibm.com/developerworks/cn/i/robbins.gif)
Daniel Robbins 居住在新墨西哥州的 Albuquerque。他是 Gentoo Technologies, Inc. 的总裁兼 CEO, Gentoo Linux(用于 PC 的高级 Linux)和 Portage 系统(Linux 的下一代移植系统)的创始人。他还是 Macmillan 书籍 Caldera OpenLinux Unleashed、 SuSE Linux Unleashed 和 Samba Unleashed 的合作者。Daniel 自二年级起就与计算机某些领域结下不解之缘,那时他首先接触的是 Logo 程序语言,并沉溺于 Pac-Man 游戏中。这也许就是他至今仍担任 SONY Electronic Publishing/Psygnosis 的首席图形设计师的原因所在。Daniel 喜欢与妻子 Mary 和新出生的女儿 Hadassah 一起共度时光。可通过 drobbins@gentoo.org 与 Daniel 联系。
相关文章推荐
- 通用线程:Awk 实例,第 2部分
- zz 通用线程: awk 实例,第 1 部分
- 通用线程:Awk 实例,第 2部分
- 通用线程:awk 实例,第 1 部分:一种名称很奇特的优秀语言介绍
- zz 通用线程:Awk 实例,第 3部分
- 通用线程:Awk 实例,第 1部分
- 通用线程:POSIX 线程详解,第 2部分
- 通用线程:POSIX 线程详解,第 2部分
- 通用线程:POSIX 线程详解,第 2部分
- 通用线程:POSIX 线程详解,第 2部分
- Awk 实例,第 2 部分
- Awk 实例,第 2 部分
- 通用线程:Awk 实例 1
- 通用线程:Awk 实例 2
- Awk 实例,第 2 部分
- Awk 实例,第 2 部分
- 通用线程:Awk 实例 3
- [引用]通用线程:POSIX 线程详解,第 2部分
- 通用线程: OpenSSH 密钥管理,第 2 部分
- 通用线程:POSIX 线程详解,第 2部分 互斥对象