AWK编程语言

6.1. gawk上路
6.1.1. 什么是gawk?

Gawk 是通常在UNIX系统下使用的另外一个流行的流编辑器 awk 的GNU版本。尽管 awk 程序常常只是一个 gawk 的连接,但是我们还是称作它为 awk。

awk 的最基本的作用是搜索含有1个或者多个模板的文件中的行或者其他文本块。当一行符合搜索的patterns,在该行就会实现指定的动作。

在 awk 中的程序和许多其他语言中的程序是不一样的,因为 awk 程序是 “数据驱动的” :你先描述你想处理的数据,然后当找到它们的时候怎么处理。许多其他的语言是“过程的”。你需要具体的描述程序该采取的措施。当使用过程化的语言时,通常更难清楚地描述你需要处理的数据。正因为如此, awk 程序经常能清爽容易地读写。
6.1.2. Gawk命令

当你运行 awk 的时候,你指定一个 awk 程序 来通知 awk 该如何做。程序由一系列 规则 组成。(也可能包含函数定义,循环,条件和其他程序结构,高级特性等一系列我们已经遗忘的东西。)每个规则指定了搜索的模板和在搜索到一个模板后执行的动作。

有几种运行 awk 的方法。如果程序很短,最方便的是在命令行运行它:

awk PROGRAM inputfile(s)

在已经完成的多个改变,通常在多个文件,很方便把 awk 命令放到脚本当中,读起来像这样:

awk -f PROGRAM-FILE inputfile(s)

6.2.1. 打印选择的域

awk 中的 print 命令把输入文件中选择的数据输出。

当 awk 读取文件的一行,根据指定的 输入域分隔符 把行分开, FS,awk 的一个环境变量(见 第 6.3.2 节 “输出分隔符”)。它被预先定义为一个或者多个空格或者制表符。

变量 $1, $2, $3, ..., $N 把输入行的第一第二第三直到最后一个域保存起来。变量 $0 把整行的值保存起来。在下面的图片描述中,我们看到 df 命令输出有6栏。

图 6.1. awk中的块
由awk解释的块:第一栏是$1,第二栏是$2,以此类推。

在 ls -l 的输出,有9栏。 print 语句像这样使用这些域:

kelly@octarine ~/test> ls -l | awk '{ print $5 $9 }'
160orig
121script.sed
120temp_file
126test
120twolines
441txt2html.sh

kelly@octarine ~/test>

这个命令把包含文件大小的第五栏和包含文件名的最后一栏打印出来。除非你使用正式的方法来用一个逗号来分割你想打印的列,否则输出并不十分适合阅读。这样的情况下,默认的输出分割字符,通常是一个空格,将被放入每个输出域。
6.2.2. 格式化域

没有格式化,只使用输出分隔符的话看上去比较破,插入几个制表符和指示输出的标记会使它变得漂亮很多:

kelly@octarine ~/test> ls -ldh * | grep -v total | \
awk '{ print "Size is " $5 " bytes for " $9 }'
Size is 160 bytes for orig
Size is 121 bytes for script.sed
Size is 120 bytes for temp_file
Size is 126 bytes for test
Size is 120 bytes for twolines
Size is 441 bytes for txt2html.sh

kelly@octarine ~/test>

注意反斜杠的用法,让shell不把它翻译成分隔命令而使其在下一行继续很长的输入。虽然命令行的输入实际上是没有长度限制的,但是你的显示器不是,打印的纸当然也不是。使用反斜杠也允许拷贝和粘贴以上行到一个终端窗口。

ls 的 -h 选项用来支持把大文件的字节数转换成更容易读的格式。当把目录作为参数的时候长列表的输出显示目录中快的总数。这行对我们并没有什么用处,所以我们加上一个*。同样的原因我们同样加上 -d 选项,万一*对一个目录展开。

在这个例子中的反斜杠提示了一行的延长。见 第 3.3.2 节 “转义字符”。

甚至在反序中你也能取出任何栏的的数字。下面的例子证明了最危险的区分。

kelly@octarine ~> df -h | sort -rnk 5 | head -3 | \
awk '{ print "Partition " $6 "\t: " $5 " full!" }'
Partition /var  : 86% full!
Partition /usr  : 85% full!
Partition /home : 70% full!

kelly@octarine ~>

下表给出了特殊格式化字符的总揽:

表 6.1. gawk的格式化字符
转义字符     含义
\a     Bell character
\n     Newline character
\t     Tab

引用,$和其他元字符应该使用反斜杠来进行转义。
6.2.3. print命令和正则表达式

用斜杠把正则表达式包含起来可以当作一个pattern。然后正则表达式测试整个文本的每条记录。语法如下:

awk 'EXPRESSION { PROGRAM }' file(s)

下面的例子现实了只有本地磁盘设备的信息,网络文件系统没有显示:

kelly is in ~> df -h | awk '/dev\/hd/ { print $6 "\t: " $5 }'
/       : 46%
/boot   : 10%
/opt    : 84%
/usr    : 97%
/var    : 73%
/.vol1  : 8%

kelly is in ~>

斜杠也需要转义,因为对于 awk 它们有着特殊的含义。

下面的另外一个例子是我们在 /etc 目录搜索以 “.conf” 结尾和 “a” 或者 “x” 开头的文件,使用扩展的正则表达式:

kelly is in /etc> ls -l | awk '/\<(a|x).*\.conf$/ { print $9 }'
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf

kelly is in /etc>

这个例子说明了在正则表达式中.的特殊意义。第一个表明了我们想要搜索在第一个搜索字符串之后的任何字符,第二个因为是要查找字符串的一部分所以被转义了(文件名的结束)。
6.2.4. 特殊的patterns

为了在输出之前加上注释,使用BEGIN语句:

kelly is in /etc> ls -l | \
awk 'BEGIN { print "Files found:\n" } /\<[a|x].*\.conf$/ { print $9 }'
Files found:
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf

kelly is in /etc>

加上 END 语句能插入文本在整个输入被处理之后:

kelly is in /etc> ls -l | \
awk '/\<[a|x].*\.conf$/ { print $9 } END { print \
"Can I do anything else for you, mistress?" }'
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf
Can I do anything else for you, mistress?

kelly is in /etc>

6.2.5. Gawk脚本

由于往往命令都有点长,你可能想把他们放到脚本中,来重用它。一个 awk 脚本包含定义pattern和动作的awk语句。

作为一个说明,我们将建立一个报告来显示占用率最高的分区,参见 第 6.2.2 节 “格式化域”。

kelly is in ~> cat diskrep.awk
BEGIN { print "*** WARNING WARNING WARNING ***" }
/\<[8|9][0-9]%/ { print "Partition " $6 "\t: " $5 " full!" }
END { print "*** Give money for new disks URGENTLY! ***" }

kelly is in ~> df -h | awk -f diskrep.awk
*** WARNING WARNING WARNING ***
Partition /usr  : 97% full!
*** Give money for new disks URGENTLY! ***

kelly is in ~>

awk 先打印一个开始信息,然后格式化所有包含一个8或者9开头的词的行,然后后接一个数字和百分符号,然后加入结束信息。
[注意]     语法高亮

Awk是一个编程语言。他的语法能被大多数能对其他语言比如c,bash,HTML进行语法高亮编辑器所识别。

 » 相关连接:
awk使用手册 CNSCN AWK使用总结 awk中文手册 和系统命令相同的awk命令
控制圈while/until 函数function 返回状态Exit 分类以下字段
得到从1~10的数字 创建屏幕输出 大小写转换 awk
[回复]
6.3. Gawk变量

既然 awk 处理输入文件,他使用几个变量。一些是可以编辑的,一些是只读的。
6.3.1. 输入域的分隔符

域分隔符,既不是一个单独的字符也不是一个普通的表达式,是控制 awk 把一个输入分割成几个域。输入记录按分割定义进行字符顺序扫描;域就是在相符的那些文字中间的那部分。

域分隔符代表内建的变量 FS,注意POSIX标准的shell使用的变量 IFS 和他是有一些区别的。

域分隔符变量的值可以在 awk 程序中用赋值操作符 = 来改变。通常最好的执行时间是一开始也就是还没有处理任何输入的时候,因此第一个记录就被随合适的分隔符一起读取。要这么做,清使用特殊的 BEGIN pattern。

以下的例子,我们编制了一条命令来显示系统里的所有用户及其描述:

kelly is in ~> awk 'BEGIN { FS=":" } { print $1 "\t" $5 }' /etc/passwd
--output omitted--
kelly    Kelly Smith
franky    Franky B.
eddy    Eddy White
willy    William Black
cathy    Catherine the Great
sandy    Sandy Li Wong

kelly is in ~>

在一个 awk 脚本中,它看起来像这样:

kelly is in ~> cat printnames.awk
BEGIN { FS=":" }
{ print $1 "\t" $5 }

kelly is in ~> awk -f printnames.awk /etc/passwd
--output omitted--

小心地选择输入分隔域来防止出现问题。一个例子来说明这个:说你需要输入想这样的行的形式:

“Sandy L. Wong, 64 Zoo St., Antwerp, 2000X”

你这样写了一个打印出记录中人的名字的命令行或者是脚本:

awk 'BEGIN { FS="," } { print $1, $2, $3 }' inputfile

但是可能一个人有PhD,而且可能写成这样:

“Sandy L. Wong, PhD, 64 Zoo St., Antwerp, 2000X”

你的 awk 会给出错误的输出。需要的话,使用额外的一个 awk 或者 sed 来统一数据输出的格式。

默认的输入分隔符是一个或多个空格和制表符。
6.3.2. 输出分隔符
6.3.2.1. 输出域分隔符

在输出中域通常被空格分隔。当你对 print 命令使用正确的语法且字段用逗号分隔时,这将很明显的:

kelly@octarine ~/test> cat test
record1        data1
record2        data2

kelly@octarine ~/test> awk '{ print $1 $2}' test
record1data1
record2data2

kelly@octarine ~/test> awk '{ print $1, $2}' test
record1 data1
record2 data2

kelly@octarine ~/test>

如果你不输入逗号, print 将把输出的项目全部当成一个字段,因此省略默认输出 分隔符, OFS的使用。

在摄制了这个内建变量的值之后任何字符串可以被用作一个输出域。
6.3.2.2. 输出记录分隔符

整个 print 语句的输出叫做输出记录。每个在输出记录里的print命令的结果,输出一个叫做 输出记录分隔符 ORS的字符串。这个变量的默认值是 “\n”,一个换行符。因此,每个 print 语句生成一个单独的行。

要改变输出域和记录的,只要给 OFS 和 ORS 赋新的值:

kelly@octarine ~/test> awk 'BEGIN { OFS=";" ; ORS="\n-->\n" } \
{ print $1,$2}' test
record1;data1
-->
record2;data2
-->

kelly@octarine ~/test>

如果 ORS 的值不包含换行,那么程序中的输出就会输出成一个单独行。
6.3.3. 记录的数量

内建的 NR 包含了处理过的记录的数量。在读入一个新的输入行之后他会自行增加一次。你可以用它来计算记录的总数,或者在每个输出记录中:

kelly@octarine ~/test> cat processed.awk
BEGIN { OFS="-" ; ORS="\n--> done\n" }
{ print "Record number " NR ":\t" $1,$2 }
END { print "Number of records processed: " NR }

kelly@octarine ~/test> awk -f processed.awk test
Record number 1:        record1-data1
--> done
Record number 2:        record2-data2
--> done
Number of records processed: 2
--> done

kelly@octarine ~/test>

6.3.4. 用户定义的变量

除了内建的变量之外,你也可以定义自己的变量。当 awk 碰到一个不存在的变量(没有事先定义的)的引用时,这个变量就被创建并且使用一个空字符串进行赋值。对于后来所有的引用,就是该变量最后被赋予的那个值。变量可以是一个字符串或者一个数字。输入域的内容也可以被赋予变量。

值可以直接用 = 来赋值,或者你可以使用现有变量的值和其他操作符组合:

kelly@octarine ~> cat revenues
20021009        20021013        consultancy    BigComp        2500
20021015        20021020        training        EduComp        2000
20021112        20021123        appdev          SmartComp      10000
20021204        20021215        training        EduComp        5000

kelly@octarine ~> cat total.awk
{ total=total + $5 }
{ print "Send bill for " $5 " dollar to " $4 }
END { print "---------------------------------\nTotal revenue: " total }

kelly@octarine ~> awk -f total.awk test
Send bill for 2500 dollar to BigComp
Send bill for 2000 dollar to EduComp
Send bill for 10000 dollar to SmartComp
Send bill for 5000 dollar to EduComp
---------------------------------
Total revenue: 19500

kelly@octarine ~>

类似于C的简写 VAR+= value 也是可以接受的。
6.3.5. 更多例子

当我们使用 awk 脚本时, 第 5.3.2 节 “写输出文件” 的例子会变得更加容易:

kelly@octarine ~/html> cat make-html-from-text.awk
BEGIN { print "<html>\n<head><title>Awk-generated HTML</title></head>\n<body bgcolor=\"#ffffff\">\n<pre>" }
{ print $0 }
END { print "</pre>\n</body>\n</html>" }

而且当用 awk 来替代 sed 后,命令也变得更加直截了当:

kelly@octarine ~/html> awk -f make-html-from-text.awk testfile > file.html

[提示]     在你系统上的awk例子。

我们再次回到包含你系统启动脚本的目录。输入一个和以下相似的命令来查看更多 awk 命令的使用方法:

grep awk /etc/init.d/*
6.3.6. printf程序

为了更精确的控制 print 提供的正常输出格式,可以使用 printf。 printf命令可以用来指明每个项目使用的域的宽度,同时也有用于数字的多种格式选择。这一切只要增加一个字符串,叫做格式字符串,控制怎样和那里来打印那些项目。

语法也和C语言的 printf 语句相似;请察看你的C介绍手册。 gawk 信息页面包含完整的解释。

 » 相关连接:
awk使用手册 CNSCN AWK使用总结 awk中文手册 和系统命令相同的awk命令
控制圈while/until 函数function 返回状态Exit 分类以下字段
得到从1~10的数字 创建屏幕输出 大小写转换 awk
 » 本栏目最新帖:
 » 精华帖:

Powered by PHPWind v6.0 Code © 2003-08