一个关于awk的介绍
时间:2006-10-24 来源:phpasp
(原文出处http://www.linuxjournal.com/article/8913)
这些日子,不是每个人都学或用awk,这里给出一个awk的简单回顾,它能做什么以及它的一些特色。
-----------------------------------------------------------
awk编程语言经常为perl这个更强的语言所鄙视。但实际上,awk比perl更常用。awk的学习曲线也没有perl那么陡,用作系统监视的脚本,效率是关键,awk也到处可见。这个简明教程被设计用来帮助你开始熟悉awk编程。
基本概念
--------
awk语言是一个小巧的,类C风格的编程语言,用来处理有固定格式文本的。通常包括数据库dump文件,系统日志。awk围绕正则表达式和模式匹配构建,这一点很象perl。实际上,perl可以被看做awk孙子辈的语言。
awk有趣的名字来自于它三位原作者的名字,Alfred V. Aho, Brian W. Kernighan 和Peter J. Weinberger。可能大多数人认识Kernighan的名字;他是C语言的创建者之一,也是UNIX世界的一只主要力量。
在命令行使用awk
---------------
我最初使用awk来列出输出的特定列。这个功能工作的出奇的好,但是当我写需要几分钟来执行的大型脚本时,脚本的效率却成了问题。尽管如此,这里给出我早期的awk代码,作为一个例子:
ls -l /tmp/foobar | awk '{print $1"\t"$9}'
这个代码需要一些输入,比如:
-rw-rw-rw- 1 root root 1 Jul 14 1997 tmpmsg
生成如下输出:
-rw-rw-rw- tmpmsg
如所示,这个代码只输出原输入的第一和第九列。因此可以看出为什么awk在做命令行数据抽取用途方面这么受欢迎。现在,让我们来看一个完整的awk程序。
awk程序的结构
-------------
我最喜爱的事情之一是awk令人惊奇的易读性,特别较之于Perl或Python更是如此。每个awk程序有三部分:Begin块,在任何输入读进来之前执行;主循环,每读进来一行就执行一次;和END块,所有输入都被读进后执行。这非常自然,这也是我经常垮在awk的地方。
这里给出一个awk的简单程序以突出awk的语言特色。在我们剖析这段代码之前,你可以看看自己能否读懂这段代码做什么:
#!/usr/bin/awk -f
#
# check the sulog for failures..
# copyright 2001 (c) jose nazario
#
# works for Solaris, IRIX and HPUX 10.20
BEGIN {
print "--- checking sulog"
failed=0
}
{
if ($4 == "-") {
print "failed su:\t"$6"\tat\t"$2"\t"$3
failed=failed+1
}
}
END {
print "---------------------------------------"
printf("\ttotal number of records:\t%d\n", NR)
printf("\ttotal number of failed su's:\t%d\n",failed)
}
想出来了吗?也许了解了输入文件sulog的典型格式行会有帮助。比如来自IRIX的sulog典型的两行:
SU 01/30 13:15 - ttyq1 jose-root
SU 01/30 13:15 + ttyq1 jose-root
现在再读一遍这段脚本,仔细想想它都做什么。BEGIN块设置所有东西,打印页眉设置一个变量,这里叫failed,为0。然后主循环读输入的每一行-这里是sulog文件,记录su企图的log文件-并且拿第四列的值和减号比较。如果是减号,意味着一次失败的尝试,因此我们就把计数器升一,并记下哪次尝试失败,什么时候。最后,最终记账符-NR,被请出来表明输入的总行数,一个awk内部变量-与失败的su尝试次数,正如我们提到的。输出看起来是这样:
failed su: jose-root at 01/30 13:15
---------------------------------------
total number of records: 272
total number of failed su's: 73
你也应该能看出printf是如何工作的。awk中printf的行为和C中printf的行为几乎一致。简言之,awk是一个相当自然的语言。
列分隔符默认是空白,你也可以自己设定。例如我在password文件里把列分隔符设为冒号。下面这一小段脚本寻找没有设密码或者ID为0(相当于root)的用户。
#!/usr/bin/awk -f
BEGIN { FS=":" }
{
if ($3 == 0) print $1
if ($2 == "") print $1
}
其他你应该知道的awk内部变量是“RS”,记录分隔符,默认值为换行符或着说“\n”;“OFS”,输出列分隔符,默认为无;和“ORS”输出记录分隔符,默认值为换行符。当然所有这些内部变量都可以在脚本里自己设置。
正则表达式
----------
awk语言匹配标准正则表达式,你已经要知道并喜爱,并且它做的比grep好。比如,我用下面的awk程序在Intel Linux系统上寻找一个可能使用的出现:
#!/usr/bin/awk -f
{ if ($0 ~ /\x90/) print "exploit at line " NR }
你不可能用grep去寻找十六进制值0x90,但0x90在Intel开发中很流行。它是NOP调用,被用作shell代码部分的填充。
虽然你可以使用awk去找十六进制值通过\xdd,这里dd是十六进制数。你也可以寻找十进制ASCII码值通过\ddd,这里ddd是十进制值。基于文本的正则表达式也是工作的。
awk随机数
---------
在awk里很容易生成随机数,但有点有意思的事要说说。函数rand()工作地恰如你所希望-它返回一个随机数,在这个例子里,产生的随机数在0和1之间。当然你也可以调整尺度,以得到更大的随机数。这有段例子代码展示这点有意思的事:
#!/usr/bin/awk -f
{
for(i=1;i<=10;i++)
print rand(); exit
}
跑几次很快就会发现问题:随机数几乎不随机-每次产生的随机数都一样!
问题出在哪呢?唉,我们没有种下随机数产生器。通常,我们我们的随机数产生器从一个好的源,比如linux下的/dev/random,获得熵。为了真正得到随机数,我们应该种下我们的随机数产生器。优化代码如下:
#!/usr/bin/awk -f
BEGIN {
srand()
}
{
for(i=1;i<=10;i++)
print rand(); exit
}
在BEGIN块我们种下了随机数产生器。函数srand()可以接受一个参数,如果没有参数,当前日期和时间被用作种子。注意相同的种子总是会产生相同的“随机”序列。
结论
----
这并不是你能找到对awk最详细的介绍,但我希望它能让你在写awk程序的时候更清楚。我自己用awk编程相当happy,并且我也要学习很多东西。我们还没有讲到数组,内建函数以及其他复杂一些的语言特色。有充足的理由讲,awk几乎不是perl的小弟弟。
资源
----
Kernighan的主页(http://cm.bell-labs.com/who/bwk/)有很多好的awk书籍列表,“真正awk”,比如nawk,的源代码。还包括很多其他来自Kernighan的有趣链接和信息。
标准awk的实现,nawk(http://cm.bell-labs.com/who/bwk/awk.tar.gz) (意思是new awk,相对于old awk而言,有时会发现oawk作为对应),基于POSIX awk规范。它还包含一个有其他两个awk,gawk和mawk实现引入的新功能。我通常用它来测试我的awk脚本的可移植性。nawk在商业UNIX机器上随处可见,通常这些地方并没有安装gawk。
GNU项目awk,gawk(ftp://gnudist.gnu.org/gnu/gawk/gawk-3.0.6.tar.gz),也是一个基于POSIX awk规范的版本,但它增加了大量有用扩展。这些包括命令行功能入“lint”对结构POSIX状态检查和恢复。我最喜爱的gawk特色是用\来断行,以及正则表达式的扩展。gawk文档力有一个关于GNU宽展awk语言的完整的讨论。gawk也是linux和BSD系统标准的awk版本。
sed & awk(http://www.oreilly.com/catalog/sed2)或许是最关于这两个语言最流行的书,受到高度关注。它包括,和其他东西一起,一个关于流行awk实现的讨论-gawk,nawk,mawk-一个功能的巨大选择和同窗O'Reilly 书的可读性。awk的主页列出基本其他的关于awk编程语言的书,但这本仍然是我的最爱。
Copyright (c) 2001, Jose Nazario. Originally published in Linux Gazette issue 67. Copyright (c) 2001, Specialized Systems Consultants, Inc.
这些日子,不是每个人都学或用awk,这里给出一个awk的简单回顾,它能做什么以及它的一些特色。
-----------------------------------------------------------
awk编程语言经常为perl这个更强的语言所鄙视。但实际上,awk比perl更常用。awk的学习曲线也没有perl那么陡,用作系统监视的脚本,效率是关键,awk也到处可见。这个简明教程被设计用来帮助你开始熟悉awk编程。
基本概念
--------
awk语言是一个小巧的,类C风格的编程语言,用来处理有固定格式文本的。通常包括数据库dump文件,系统日志。awk围绕正则表达式和模式匹配构建,这一点很象perl。实际上,perl可以被看做awk孙子辈的语言。
awk有趣的名字来自于它三位原作者的名字,Alfred V. Aho, Brian W. Kernighan 和Peter J. Weinberger。可能大多数人认识Kernighan的名字;他是C语言的创建者之一,也是UNIX世界的一只主要力量。
在命令行使用awk
---------------
我最初使用awk来列出输出的特定列。这个功能工作的出奇的好,但是当我写需要几分钟来执行的大型脚本时,脚本的效率却成了问题。尽管如此,这里给出我早期的awk代码,作为一个例子:
ls -l /tmp/foobar | awk '{print $1"\t"$9}'
这个代码需要一些输入,比如:
-rw-rw-rw- 1 root root 1 Jul 14 1997 tmpmsg
生成如下输出:
-rw-rw-rw- tmpmsg
如所示,这个代码只输出原输入的第一和第九列。因此可以看出为什么awk在做命令行数据抽取用途方面这么受欢迎。现在,让我们来看一个完整的awk程序。
awk程序的结构
-------------
我最喜爱的事情之一是awk令人惊奇的易读性,特别较之于Perl或Python更是如此。每个awk程序有三部分:Begin块,在任何输入读进来之前执行;主循环,每读进来一行就执行一次;和END块,所有输入都被读进后执行。这非常自然,这也是我经常垮在awk的地方。
这里给出一个awk的简单程序以突出awk的语言特色。在我们剖析这段代码之前,你可以看看自己能否读懂这段代码做什么:
#!/usr/bin/awk -f
#
# check the sulog for failures..
# copyright 2001 (c) jose nazario
#
# works for Solaris, IRIX and HPUX 10.20
BEGIN {
print "--- checking sulog"
failed=0
}
{
if ($4 == "-") {
print "failed su:\t"$6"\tat\t"$2"\t"$3
failed=failed+1
}
}
END {
print "---------------------------------------"
printf("\ttotal number of records:\t%d\n", NR)
printf("\ttotal number of failed su's:\t%d\n",failed)
}
想出来了吗?也许了解了输入文件sulog的典型格式行会有帮助。比如来自IRIX的sulog典型的两行:
SU 01/30 13:15 - ttyq1 jose-root
SU 01/30 13:15 + ttyq1 jose-root
现在再读一遍这段脚本,仔细想想它都做什么。BEGIN块设置所有东西,打印页眉设置一个变量,这里叫failed,为0。然后主循环读输入的每一行-这里是sulog文件,记录su企图的log文件-并且拿第四列的值和减号比较。如果是减号,意味着一次失败的尝试,因此我们就把计数器升一,并记下哪次尝试失败,什么时候。最后,最终记账符-NR,被请出来表明输入的总行数,一个awk内部变量-与失败的su尝试次数,正如我们提到的。输出看起来是这样:
failed su: jose-root at 01/30 13:15
---------------------------------------
total number of records: 272
total number of failed su's: 73
你也应该能看出printf是如何工作的。awk中printf的行为和C中printf的行为几乎一致。简言之,awk是一个相当自然的语言。
列分隔符默认是空白,你也可以自己设定。例如我在password文件里把列分隔符设为冒号。下面这一小段脚本寻找没有设密码或者ID为0(相当于root)的用户。
#!/usr/bin/awk -f
BEGIN { FS=":" }
{
if ($3 == 0) print $1
if ($2 == "") print $1
}
其他你应该知道的awk内部变量是“RS”,记录分隔符,默认值为换行符或着说“\n”;“OFS”,输出列分隔符,默认为无;和“ORS”输出记录分隔符,默认值为换行符。当然所有这些内部变量都可以在脚本里自己设置。
正则表达式
----------
awk语言匹配标准正则表达式,你已经要知道并喜爱,并且它做的比grep好。比如,我用下面的awk程序在Intel Linux系统上寻找一个可能使用的出现:
#!/usr/bin/awk -f
{ if ($0 ~ /\x90/) print "exploit at line " NR }
你不可能用grep去寻找十六进制值0x90,但0x90在Intel开发中很流行。它是NOP调用,被用作shell代码部分的填充。
虽然你可以使用awk去找十六进制值通过\xdd,这里dd是十六进制数。你也可以寻找十进制ASCII码值通过\ddd,这里ddd是十进制值。基于文本的正则表达式也是工作的。
awk随机数
---------
在awk里很容易生成随机数,但有点有意思的事要说说。函数rand()工作地恰如你所希望-它返回一个随机数,在这个例子里,产生的随机数在0和1之间。当然你也可以调整尺度,以得到更大的随机数。这有段例子代码展示这点有意思的事:
#!/usr/bin/awk -f
{
for(i=1;i<=10;i++)
print rand(); exit
}
跑几次很快就会发现问题:随机数几乎不随机-每次产生的随机数都一样!
问题出在哪呢?唉,我们没有种下随机数产生器。通常,我们我们的随机数产生器从一个好的源,比如linux下的/dev/random,获得熵。为了真正得到随机数,我们应该种下我们的随机数产生器。优化代码如下:
#!/usr/bin/awk -f
BEGIN {
srand()
}
{
for(i=1;i<=10;i++)
print rand(); exit
}
在BEGIN块我们种下了随机数产生器。函数srand()可以接受一个参数,如果没有参数,当前日期和时间被用作种子。注意相同的种子总是会产生相同的“随机”序列。
结论
----
这并不是你能找到对awk最详细的介绍,但我希望它能让你在写awk程序的时候更清楚。我自己用awk编程相当happy,并且我也要学习很多东西。我们还没有讲到数组,内建函数以及其他复杂一些的语言特色。有充足的理由讲,awk几乎不是perl的小弟弟。
资源
----
Kernighan的主页(http://cm.bell-labs.com/who/bwk/)有很多好的awk书籍列表,“真正awk”,比如nawk,的源代码。还包括很多其他来自Kernighan的有趣链接和信息。
标准awk的实现,nawk(http://cm.bell-labs.com/who/bwk/awk.tar.gz) (意思是new awk,相对于old awk而言,有时会发现oawk作为对应),基于POSIX awk规范。它还包含一个有其他两个awk,gawk和mawk实现引入的新功能。我通常用它来测试我的awk脚本的可移植性。nawk在商业UNIX机器上随处可见,通常这些地方并没有安装gawk。
GNU项目awk,gawk(ftp://gnudist.gnu.org/gnu/gawk/gawk-3.0.6.tar.gz),也是一个基于POSIX awk规范的版本,但它增加了大量有用扩展。这些包括命令行功能入“lint”对结构POSIX状态检查和恢复。我最喜爱的gawk特色是用\来断行,以及正则表达式的扩展。gawk文档力有一个关于GNU宽展awk语言的完整的讨论。gawk也是linux和BSD系统标准的awk版本。
sed & awk(http://www.oreilly.com/catalog/sed2)或许是最关于这两个语言最流行的书,受到高度关注。它包括,和其他东西一起,一个关于流行awk实现的讨论-gawk,nawk,mawk-一个功能的巨大选择和同窗O'Reilly 书的可读性。awk的主页列出基本其他的关于awk编程语言的书,但这本仍然是我的最爱。
Copyright (c) 2001, Jose Nazario. Originally published in Linux Gazette issue 67. Copyright (c) 2001, Specialized Systems Consultants, Inc.
相关阅读 更多 +