1. awk简介 awk是用来操作数据和产生报表的一种编程语言。数据可能来自标准输入、一个或者多个文件或者是一个进程的输出。awk可以用在命令行里用于简单操作,或者可以为了较大的应用而写到程序中。常见的awk命令有 awk、nawk等。awk从第1行到最后一行逐行扫描文件(或输入),并执行选定的操作(封装在花括号里)。本文章所有的例子使用的文件未经说明都在chap05目录下。 2. awk的格式 awk程序由 awk命令,封装在引号里(或在一个文件里)的程序指令,和输入文件组成。如果没有指定一个输入文件,则输入来自标准输入(stin)键盘。 现在来看employees文件中的内容: $ cat employees Tom Jones 4424 5/12/66 543354 Mary Adams 5346 11/4/63 28765 Sally Chang 1654 7/22/54 650000 Billy Black 1683 9/23/44 336500 1) 从文件输入 $ awk '/Mary/' employees Mary Adams 5346 11/4/63 28765 解释:打印employees文件中含有Mary的行 $ awk '{print $1}' employees Tom Mary Sally Billy 解释:打印文件employees中的第一个域。域的分隔符是空格。 $ nawk '/Sally/{print $1,$2}' employees 解释:只有找到了Sally的行才打印第一个域和第二个域。 2) 管道输入 $ df -k|awk '$4>1024000' 解释:报告剩余空间大于1024000k的盘。 3. 格式化输出 1) print函数 例子1: $ date 2005年04月30日 星期六 19时29分25秒 CST $ date | awk '{print "Date:" $1 "\nTime:" $3}' Date:2005年04月30日 Time:19时34分24秒 注意,用 date 命令查看时间格式。不同的语言可能格式也不一样,所以awk也要随之而变。\n是转义序列,表示换行符。常见转义序列如下:
| 转义序列 |
含义 |
| \b |
退格 |
| \f |
换页 |
| \n |
换行 |
| \r |
回车 |
| \t |
跳格 |
| \047 |
八进制值47,一个单引号 |
| \c |
C代表任意其它字符,例如\'' |
例子2: $ nawk '/Sally/{print "\t\tHave a nice day, "$1,$2 "\!"}' employees Have a nice day, Sally Chang! 解释:如果包含模式Sally,则print函数打印两个跳格,串Have a nice day,第一个域和第二个域,然后跟叹号。 OFMT 变量:当打印数字时,如果使用 print 函数,想控制精度时,可以用OFMT变量。默认设置是“%.6g”,也就是打印精度到小数点后6位。下面的例子可以改变精度。 $ nawk 'BEGIN{OFMT="%.2f"; print 1.23456789,12E-2}' 1.23 0.12 2) printf函数 printf函数提供了强大的格式化输出功能,如果对 c 比较熟悉,就不会感觉太陌生。 printf的转换字符
| 转换字符 |
定义 |
| c |
字符 |
| s |
串 |
| d |
十进制数 |
| ld |
长十进制数 |
| u |
无符号十进制数 |
| lu |
无符号长十进制数 |
| x |
十六进制数 |
| lx |
长十六进制数 |
| o |
八进制数 |
| lo |
长八进制数 |
| e |
以科学技术发记的符点数 |
| f |
浮点数 |
| g |
从e或f 转换中选择一种占用空间最少的记符点数 |
修饰符
| 字符 |
定义 |
| - |
左对齐修饰符 |
| # |
整数在用八进制显示前面有个 0;整数在用十六进制显示时前面有个0x |
| + |
对于用d、e、f和g的转换,带一个数字符号+或-显示整数 |
| 0 |
显示的值用 0补而不是用空格 |
printf格式说明符
| 格式符 |
功能 |
| 假设x=’A’ y=15 z=2.3 $1=Bob Smith |
| %c |
打印一个单 ASCII字符 $ printf "The character is %c\n" $x The character is A |
| %d |
打印一个十进制数 $ printf "The boy is %d years old\n" $y The boy is 15 years old |
| %e |
打印用科学计数法记的一个数 $ printf "z is %e\n" $z z is 2.300000e+00 |
| %f |
打印一个符点数 $ printf "z is %f\n" $z z is 2.300000 |
| %o |
打印一个数的八进制值 $ printf "y is %o\n" $y y is 17 |
| %s |
打印一个字符串 $ printf "The name of the culprit is %s\n" $1 The name of the culprit is Bob Smith |
| %x |
打印一个数的十六进制值 $ printf "y is %x\n" $y y is f |
例子: $ echo UNIX |awk '{printf "|%-15s|\n",$1}' |UNIX | 解释:echo命令输出从管道中送给 awk。格式说明符说明将会打印一个占15个空格,左对齐,封装在竖杠里而且有换行的串。 $ awk '{printf "The name is %-15s ID is %8d\n",$1,$3}' employees The name is Tom ID is 4424 The name is Mary ID is 5346 The name is Sally ID is 1654 The name is Billy ID is 1683 4. 在一个文件里的awk命令 如果awk命令放在文件里,就使用-f选项和awk文件名结合使用。处理过程:把一条记录读到 awk 的缓存里而且对该记录测试并执行 awk 文件里的每个命令。在 awk 完成对第一条记录的操作后,删除该记录并把下一条记录读入缓存,依此类推。如果操作不受模式控制,则默认行为是打印整个记录。 例子: $ cat employees Tom Jones 4424 5/12/66 543354 Mary Adams 5346 11/4/63 28765 Sally Chang 1654 7/22/54 650000 Billy Black 1683 9/23/44 336500 $ cat awkfile /^Mary/{print "Hello Mary!"} {print $1, $2, $3} $ nawk -f awkfile employees Tom Jones 4424 Hello Mary! Mary Adams 5346 Sally Chang 1654 Billy Black 1683 5. 记录和域 1) 记录:awk不把输入数据看作一个无穷的字符串,而是把它看作一种格式或结构。默认情况下把每行叫做一个记录(record),并以一个换行符终止。输入和输出的记录分隔符默认是回车符,保存在内置awk变量ORS和RS中。ORS和RS可以改变,但是方式有限。 例子: $ nawk '{print $0}' employees Tom Jones 4424 5/12/66 543354 Mary Adams 5346 11/4/63 28765 Sally Chang 1654 7/22/54 650000 Billy Black 1683 9/23/44 336500 解释:变量$0保存当前记录,这条命令相当于nawk '{print}' employees 例子: $ awk '{print NR,$0}' employees 1 Tom Jones 4424 5/12/66 543354 2 Mary Adams 5346 11/4/63 28765 3 Sally Chang 1654 7/22/54 650000 4 Billy Black 1683 9/23/44 336500 解释:NR代表行号。 2) 域 类似表中的字段,是指记录中的一个词条。默认域分隔符是空白区域,也就是空格或制表符(TAB)。Awk中用NF来记录每条记录的域数量。 3) 域分隔符 输入域分隔符:awk的内置变量FS保存输入域分隔符的值。当时用FS的默认值时,awk用空格或制表符分隔域,删除前导空白区和制表符。FS可以改变,可以在 BEGIN 语句中改变,也可以在命令中改变。要想在命令中改变需要用-F选项。 举例: $ cat employees2 Tom Jones:4424:5/12/66:543354 Mary Adams:5346:11/4/63:28765 Sally Chang:1654:7/22/54:650000 Billy Black:1683:9/23/44:336500 $ awk '{print $1,$2}' employees2 Tom Jones:4424:5/12/66:543354 Mary Adams:5346:11/4/63:28765 Sally Chang:1654:7/22/54:650000 Billy Black:1683:9/23/44:336500 $ awk -F: '{print $1,$2}' employees2 Tom Jones 4424 Mary Adams 5346 Sally Chang 1654 Billy Black 1683 举例2:多个域分隔符。如果使用多个域分隔符,要将其封装在方括号里。 $ nawk -F'[ :\t]' '{print $1,$2,$3}' employees2 Tom Jones 4424 Mary Adams 5346 Sally Chang 1654 Billy Black 1683 解释:把空格、制表符、冒号当成输入域分隔符。 输出域分隔符:默认的输出域分隔符用逗号来分隔,被分隔的域之间打 印一个空格,如果域之间没有逗号,则打印时各域将挤在一起。 6.模式和操作 awk 模式(patterns)控制 awk 将对一行输入作什么样的操作。一个模式包括一个正则表达式,一个产生正确或者错误条件的表达式,或者它们的组合。默认操作时打印模式中符合表达式条件的各行。当读入一个模式时,有一条隐含的if语句。如果if语句是隐含的,周围可以没有花括号。 例子: $ awk '$3<4000' employees Sally Chang 1654 7/22/54 650000 Billy Black 1683 9/23/44 336500 解释:如果第3个域小于4000,则打印该域。 操作:操作(actions)是封装在花括号里且由分号分隔的语句。如果一个模式在一个操作之前,则该模式规定了何时执行该操作。 举例: $ awk '/Tom/{print "Hello there," $1}' employees Hello there,Tom 解释:如果记录中包含Tom,则打印Hello there,Tom 7.正则表达式 一个正则表达式对awk来说是一个由封装在正斜杠里的字符组成的模式。Awk支持的正则表达式与egrep基本一样。 举例: $ nawk '/^Mary/' employees Mary Adams 5346 11/4/63 28765 解释:显示employees文件中以Mary开头的行 $ nawk '/^[A-Z][a-z]* /' employees Tom Jones 4424 5/12/66 543354 Mary Adams 5346 11/4/63 28765 Sally Chang 1654 7/22/54 650000 Billy Black 1683 9/23/44 336500 解释:显示行开头是一个大写字母,然后跟一个或多个小写字母,再跟一个空格。 匹配操作符(~)、否定号(!)用来与以条记录或域里的表达式相匹配。 例子: $ awk '$1 ~/[Bb]ill/' employees Billy Black 1683 9/23/44 336500 解释:awk将显示第一个域里与Bill或者bill相匹配的行 $ awk '$1 !~/ly$/' employees Tom Jones 4424 5/12/66 543354 Mary Adams 5346 11/4/63 28765 解释:显示第一个域中末尾不是ly的所有行。 8.写在命令表文件中的awk命令 写awk命令表的方法可以参考chap05/info文件。该文件内容如下: # Here is a sample script: #**************************************************************** * # My first awk script by Jack Sprat # Script name: info; Date: February 28, 1999 /Tom/{print "Tom's birthday is " $3} /Mary/{print NR, $0} /^Sally/{print "Hi Sally, " $1 " has a salary of $" $4 "."} #End of script 9.awk相关练习题 donors文件内容如下: Mike Harrington:(510) 548-1278:250:100:175 Christian Dobbins:(408) 538-2358:155:90:201 Susan Dalsass:(206) 654-6279:250:60:50 Archie McNichol:(206) 548-1348:250:100:175 上面的数据库包含名字、电话号码和过去三个月的竞选捐款。 1) 打印所有的电话号码 $ awk -F: '{print $2}' donors 2) 打印Dan的电话号码 $ awk -F: '/^Dan/{print $2}' donors 3) 打印Susan的名字和电话号码 $ nawk -F'[ :\t]' '/^Susan/{print $1,$3,$4}' donors 4) 打印所有以D开头的姓 $ awk '$2 ~/^D/{print $2}' donors 5) 打印所有以一个C或E开头的名 $ awk '/^[CE]/{print $1}' donors 6) 打印所有只有四个字符的名 $ nawk '/^[A-Z][a-z][a-z][a-z] /{print $1}' donors 7) 打印所有那些区号是916的名 $ awk -F: '$2 ~/916/{print $1}' donors 8) 打印 Mike 的活动捐款。打印每个值时都要以一个美元符号打头;例如: $500$200$300 $ awk -F: '/^Mike/{printf "$%d$%d$%d\n",$3,$4,$5}' donors 9) 打印姓,其后跟一个逗号和名 $ nawk -F'[ :\t]' '{printf "%s,%s\n",$2,$1}' donors 10) 写一个叫facts的awk命令表,它能: a. 打印Savages的全名和电话号码 b. 打印Chet的捐款 c. 打印所有第一个月捐款$250的人 文件内容如下: $ cat facts #My first awk scripts by wangzhh. /Savage/{print $1,$2} /^Chet/{printf "Beneficence of Chet: $%d$%d$%d\n",$3,$4,$5} $3 ~/250/{printf "Person who's beneficence is 250:%s\n",$1} 调用方法: nawk -F: -f facts donors |