Awk实例 第一部分

起因:近日google一把awk的教程,翻到IBM developerworks上一个初级的awk系列文章,是Gentoo Linux创始人在多年前写的。细看一下觉得很不错,可developerworks中国上面的中文版却是缺斤少量,比起原文整段整段的丢失,让人看得糊涂。不知是机器翻译问题还是版面问题?于是只有去翻看英文版(本人英语那个差啊,逼着看),遂有重新翻译一遍,权当为自己保留,美国空间,也为其他有需要的朋友保留吧。尽管是初级内容,还是很值得一看,确实没想到Gentoo的创始人会写这么基础的东西来普渡众生。

原文地址:

Common threads: Awk by example, Part 1

一个名字怪怪的强大语言介绍

概要:awk是一个有着奇怪名字的强大语言。本文是这个系列中的第一篇,本系列共包括三篇文章。本篇中Daniel将带你快速掌握awk的编程技巧,随着系列的进展,将包括更多高级的主题,最终演示一个真实的高级awk应用程序。

为AWK辩护

在这一系列文章中,我将带领你成为一个熟练的awk程序员。我承认awk并没有一个漂亮又时尚的名字,并且GNU版的awk(名叫gawk)听起来也很奇怪。那些不熟悉这个语言的人听到awk就会想到一团乱糟糟的落伍又过时的代码,他们认为它甚至导致最博学的UNIX专家也到了疯狂的边缘(引起他们不断的使用“kill -9!”,就像使用咖啡机一样)。

确实,awk没有动听的名字,但他是一个强大的语言。AWK很适合文字处理和报表生成,还有很多精心设计的特性允许进行严谨的编程。并且不像其他有些语言,awk的语法是你很熟悉的,他借用了C、python和bash等语言的一些最好部分(尽管在技术上awk比起python和bash来说出现得要早)。 AWK是这样一种语言——你一旦学会,他将会成为你编程战略库中的一个重要部分。

第一个awk程序

让我们开始来玩玩awk,看他是如何工作的吧。在命令行输入下面的命令:

$ awk ‘{ print }’ /etc/passwd

你将看到你的/etc/passwd文件出现在你的眼前。现在我们来解释一下awk做了什么。我们调用awk时,我们指定/etc/passwd作为输入文件。awk执行期间,他依次对/etc/passwd中的每一行执行print命令。所有的输出都发送到标准输出,我们得到的结果跟执行cat /etc/passwd一样。现在解释一下{ print }代码块。类似C语言一样,awk中使用花括号组织语句块。在我们的代码块中只有一个print命令。awk中只有print命令单独出现的时候,当前行的所有内容都会被打印出来。

下面还有另外一个awk例子,他干上面那个语句所做的相同的事情:

$ awk ‘{ print $0 }’ /etc/passwd

在awk中,$0代表当前行整行,所以print和print $0实际上是干一样的事情。如果你喜欢,你也能创建一个awk程序来输出与输入数据完全不相干的数据。下面就是一个例子:

$ awk ‘{ print “” }’ /etc/passwd

每当你传递“”字符串给print命令时,美国空间,他打印一个空白行。如果你测试上面这个脚本,你将发现awk为/etc/passwd中的每一行都输出一个空白行。这是因为awk为输入文件的每一行都执行你的脚本。下面还有另外一个例子:

$ awk ‘{ print “hiya” }’ /etc/passwd

运行这个脚本,你的屏幕将充满hiya。:)

多个字段

Awk处理分为多个逻辑字段的文本那是真的真的很方便,awk允许你从脚本中毫不费力地引用到每个单独的字段。下面的脚本将打印出你系统中所有用户的列表:

$ awk -F”:” ‘{ print $1 }’ /etc/passwd

在上面的例子中,调用awk时,使用-F选项指定“:”作为字段分割符。当awk处理print $1命令时,他将打印出现在输入文件每行中的第一个字段。这儿是另外一个例子:

$ awk -F”:” ‘{ print $1 $3 }’ /etc/passwd

这儿是这个脚本的输出摘录:

halt7 operator11 root0 shutdown6 sync5 bin1 ….etc.

如你所见,awk打印出了/etc/passwd文件中的第一个和第三个字段,刚好是用户名和uid字段。现在,尽管这个脚本工作了,但他并不完美——两个输出字段间没有任何空格!如果你习惯在bash或python中编程,你可能期望print $1 $3命令在两个字段间插入空格。然而,awk程序中当两个字符串相邻出现时,awk不会增加中间空格来连接他们。下面的命令将在两个字段间插入空格:

$ awk -F”:” ‘{ print $1 ” ” $3 }’ /etc/passwd

当你使用这种方式调用print时,他将连接$1,” “,$3,这就创建了一个易读的输出。当然,如果有需要,我们也可以插入一些文本标签:

$ awk -F”:” ‘{ print “username: ” $1 “\t\tuid:” $3″ }’ /etc/passwd

这个命令的输出如下:

username: haltuid:7 username: operator uid:11 username: rootuid:0 username: shutdown uid:6 username: syncuid:5 username: binuid:1 ….etc.

外部脚本

对于小的单行脚本来说,作为命令行参数传给awk非常方便,但当脚本变成复杂的多行程序,你绝对想要将脚本组织在一个外部文件中。然后通过-f选项将该脚本文件传给awk:

$ awk -f myscript.awk myfile.in

将脚本放到单独的文本文件中也允许你利用awk额外的特性。例如,下面这个多行脚本与我们早前的一个单行脚本干一样的事:打印/etc/passwd中每行的的第一个字段:

BEGIN { FS=”:” } { print $1 }

这两种方法的不同之处在于我们如何设置分割符。这个脚本中,在代码内部指定分隔符(通过设置FS变量),而前面的例子则通过在awk命令行中传递-F”:”选项。仅因为这样做你可以得到少记一个命令行参数的好处,香港空间,因此在脚本中设置分隔符通常也是最好的。我们将在这篇文章中更详细的介绍FS变量

BEGIN和END块

通常情况,awk在输入文件的每行上执行脚本中每个代码块。还有在许多编程环境下,你需要在开始处理输入文件的文本前执行一些初始化动作。对于这种情况,awk允许你定义BEGIN块。我们在前面的例子中已经用过一次BEGIN块。因为BEGIN块是在awk开始处理输入文件前执行的,因此它是进行诸如初始化FS变量、打印头行或者初始化一些全局变量(程序后面会用到的变量)的好地方。

Awk也提供了一个叫END块的另一个特殊代码块。在输入文件的所有行都处理完成后awk执行这个代码块的内容。典型的,END块用来执行最后的计算或者打印那些在输出流末尾应该出现的总结性内容。

正则表达式和代码块

Awk允许使用正则表达式,根据正则表达式是否匹配当前行来有选择的执行一个代码块。这儿是一个实例脚本,用来输出那些包含字符序列“foo”的行:

/foo/ { print }

当然,你也可以使用更加复杂的正则表达式,下面这个脚本将仅打印包含浮点数的那些行:

/[0-9]+\.[0-9]*/ { print }

生气是拿别人做错的事来惩罚自己

Awk实例 第一部分

相关文章:

你感兴趣的文章:

标签云: