wangyp

博客园 首页 联系 订阅 管理
简介
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
 
流程
awk工作流程是这样的:先执行BEGING,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。
 
如图:
 
awk内置变量
awk有许多内置变量用来设置环境信息,这些变量可以被改变,下面给出了最常用的一些变量。
ARGC               命令行参数个数
ARGV               命令行参数排列
ENVIRON            支持队列中系统环境变量的使用
FILENAME           awk浏览的文件名
FNR                浏览文件的记录数
FS                 设置输入域分隔符,等价于命令行 -F选项
NF                 浏览记录的域的个数
NR                 已读的记录数
OFS                输出域分隔符 
ORS                输出记录分隔符
RS                 控制记录分隔符
ARGVIND            命令行参数索引(合并文件用过、)

  

FILENAME awk浏览的文件名NF 浏览记录的域的个数 NR 已读的记录数OFS 命令行参数索引(合并文件用过、)
Q1:打印目录下文件大小
A1:
[root@salt-node01 etc]# ls -al | awk -F " " 'BEGIN {filesize=0;print "[start]file size is",filesize;} {filesize=filesize+$5;} END{print "[end]file size is",filesize/1024,"K"}'
[start]file size is 0
[end]file size is 846.303 K
小结:
awk语法:-F配合“”指定分隔符,‘’用于指定pattern和action;BEGIN{}语句用于初始化变量和输出提示信息,第二个大括号{}中的内容,按照指定的分隔符,生成不同的域值,将field的内容进行对应的匹配和操作,最后END{}输出要表示的信息;如果在{}中需要进行for语句或者if语句,需要将循环条件或者判断条件用()进行包裹,后边紧跟{}进行statement语句。
此例中定义了一个变量filesize进行累计$5的操作,难度为1
 
Q2:打印奇数行
[root@salt-node01 awk_test]# seq 10 | awk 'i=!i'
等价于[root@salt-node01 ~]# seq 10 | awk 'BEGIN{}i=!i{print $0}END{}'
1
3
5
7
9
分析:seq 10进行数据模拟,输出1-10;awk处理流程,pattern和command的模式,此处无command,默认输出为$0,等价于seq 10 | awk '{i=!i;print $0}';此处只有pattern,pattern为i=!i; awk处理变量,对于未定义的变量,认为i是0,此处进行赋值i=1;1为真则匹配改行,打印$0;然后i=0,,,,,,所以只打印奇数行
验证:
[root@salt-node01 awk_test]# seq 10 | awk '{i=!i;print i}'
1 #打印
0 #不打印
1 #重复上述动作
0
1
0
1
0
1
0
Q3:如何打印偶数行?(BEGIN对i进行赋值即可)
[root@salt-node01 awk_test]# seq 10 | awk 'BEGIN{i=1} i=!i;'
2
4
6
8
10
Q4:行去重
进阶题:
[root@salt-node01 awk_test]# cat uniq.text
bbb
bbb
bbb
ccc
ccc
ccc
aaa
aaa
aaa
aaa
aaa
aaa
aaa 
请使用awk命令进行数据去重。
[root@salt-node01 awk_test]# awk '!a[$0]++' uniq.text
bbb
ccc
aaa
分析:awk处理流程略,处理流程见上例;此处pattern是'!a[$0]++,C语言中++是先引用后赋值,此处a[bbb]是0,取反则是1,所以为真,awk读取第一行后,经过++运算(先引用后赋值)将a[bbb]设置为1,真则执行print $0,则打印第一行,第二行处理时a[bbb]为1,取反为0,所以为假,awk读取第二行后,经过++运算,将a[bbb]设置为2,,假则不进行action,第三行处理时,a[bbb]为2,同理不打印。
验证:
[root@salt-node01 awk_test]# awk '{!a[$0]++;print a[$0]}' uniq.text
1
2
3
1
2
3
1
2
3
4
5
6
7
Q5:分别计算每个字符串后面的数字总和
[root@salt-node01 awk_test]# cat uniq.text
bbb 100
bbb 100
bbb 1
ccc 88
ccc 99
ccc 765
aaa 1
aaa 2
aaa 4
aaa 5
aaa 1008
aaa 876
aaa 123
答案:
[root@salt-node01 awk_test]# awk ' {a[$1]+=$2} END {for(i in a) print i,a[i]} ' uniq.text
aaa 2019
ccc 952
bbb 201
分析:awk问题该怎么做呢?归根到底是算法问题,比如在此例中,计算学生aaa的成绩总和,如果有个数组就方便很多,而shell外边向awk传变量也是一种思路,但是来回传递效率低,显得B格不够,此处用shell数组实现就很方便,awk是C语言的编程风格,+=是先加后赋值的语法,此处等同于a[$1]=a[$1]+$2;处理第一行,a[bbb]=0+100=100,处理第二行a[bbb]=100+100=200,处理第三行a[bbb]=200+1=201,同理,可以计算ccc bbb学生的成绩总和
Q6:计算学生成绩平均值及总和
要求输出格式:(average:平均成绩,total:总成绩)
name#######average#######total
zhangsan            xxx                        xxx
lisi                       xxx                        xxx
wangwu             xxx                        xxx
解法:
[root@localhost awk_test]# awk -F " " 'BEGIN{print "name#######average#######total"} {sum[$1]+=$2;total[$1]++} END {for (i in sum) print i,sum[i]/total[i],sum[i]}' 01.txt
name#######average#######total
zhangsan 85 255
wangwu 92.6667 278
lisi 87.1667 261.5
分析:此处BEGIN{}输出name等表头信息;{}用于域值的处理;END{}用for循环输出信息
Q7:假如把3列和4列的和值作为新的第5列,第5列的平均值为avg5,求第5列中大于avg5的行数
[root@localhost awk_test]# more 02.text
4 6 7 8
3 4 2 1
5 6 7 10
3 4 5 5
3 3 2 1
5 6 1 10
解法:
[root@localhost awk_test]# awk '{sum+=$3+$4;array[NR]=$3+$4}END{arg=sum/NR;for (i=1;i<=length(array);i++) {if (array[i]>arg) print i}}' 02.text
1
3
4
6
小结:
for语句和if语句进行循环处理时候,需要用()把条件进行包围,语法是C语言风格
if (expression) { statement; statement; ... ... }
此处是for语句的statement中嵌套了一个if语句
Q8:处理文件
02.text文件是
[root@salt-node01 awk_test.bak]# more 02.text
1.1.1.1 11
1.1.1.1 22
1.1.1.1 33
1.1.1.1 44
2.2.2.2 11
2.2.2.2 22
2.2.2.2 33
2.2.2.2 44
awk命令及输出结果:
[root@salt-node01 awk_test]# awk -F " " 'BEGIN{}{a[$1]=a[$1]" "$2}END{for (i in a){print i,a[i]}}' 02.text
1.1.1.1 11 22 33 44
2.2.2.2 11 22 33 44
分析:观察每次行读取处理后每行值的变化情况,第一次是1.1.1.1 11 第二次是1.1.1.1 11 22 ......应该是这样,用shell数组配合自增会是一个perfect的解决方案,array[$1]=array[$1]""[$2]
09:按照第三列进行排序:
[root@salt-node01 awk_test]# cat 03.text
12 34 56
78 90 12
34 56 89
方法01:sort命令
[root@salt-node01 awk_test]# cat 03.text |sort -t ' ' -k 3
78 90 12
12 34 56
34 56 89
方法02:awk方法
对于两个field的文件,用awk内置的asorti函数可以实现排序,三个field的文件,还需要进一步考虑,有时间看看。
10:连接2个文件,文件内容是:
[root@salt-node01 awk_test]# cat join.0*
100 wangyp
200 jack
300 tom
100 1000$
200 1010$
300 1100$
方法01:join
[root@salt-node01 awk_test]# join -a 1 join.01 join.02
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
方法02:awk
[root@salt-node01 awk_test]# awk 'ARGIND==1{a[$1]=$0};ARGIND==2{print a[$1],$2}' join.01 join.02
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
方法03:paste
[root@salt-node01 awk_test]# paste join.01 join.02 | awk '{print $1,$2,$NF}'
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
方法04:
[root@salt-node01 awk_test]# awk 'NR==FNR{a[$1]=$0};NR>FNR{print a[$1],$2}' join.01 join.02
100 wangyp 1000$
200 jack 1010$
300 tom 1100$
 
 
 
 
 
 
posted on 2017-05-23 13:04  wangyp  阅读(218)  评论(1)    收藏  举报