1. 程序结构: Begin 和 End模块
awk的程序的结构:Begin块,Body块,End块。
- BEGIN块:BEGIN {awk-commands}
- BEGIN块在被程序启动时启动,且只执行一次。
- 这是一个很好的初始化变量的地方,常常被用来修改内置变量RS,FS,OFS,ORS等的值。
- BEGIN是awk的关键字,必须大写。
- 可选的
BODY块: /pattern/{awk-commands(action)}
- 主体块适用于awk的每个输入行命令。
- 默认情况下,awk执行每一行命令,但可以通过pattern限制。
- body块没有关键字
- END块: END {awk-commands}
- END块在程序结束时执行,在awk读取完所有的文件的时候,再执行的。
- 一般用来输出一个结果(累加,数组结果)
- END是awk关键字,必须是大写。
- 可选的。
[root@oldboy test]# awk 'BEGIN {print "Sr No Name Sub Marks "} {print} END{print "****END***"}' marks.txt Sr No Name Sub Marks 1) Amit Physics 80 2) Rahul Maths 90 3) Shyam Biology 87 4) Kedar English 85 5) Hari History 89 ****END***
案例:统计文件里面的空行数量
# grep查看文件内的空行数量
[root@oldboy test]# grep -c '^$' /etc/services 16
# 遇到空行print 数量(增量+1) [root@oldboy test]# awk '/^$/{a=a+1;print a}END{print "Blank Record Count:",a}' /etc/services 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Blank Record Count: 16
# 仅输出END块,空行数量
[root@oldboy test]# awk '/^$/{a=a+1} END{print "Blank Record Count:",a}' /etc/services
Blank Record Count: 16
问题: awkfile2.txt 里面 以:为分隔符,区域3 大于15,一共有多少个?
awk -F ":" '$3>15{a=a+1} END{print a}' awkfile2.txt
示例:找出环境变量$PATH中,所有只有三个任意字符的命令,例如:tee,并将它们重定向到command.txt中,要求一行显示1个,并在文件尾部统计他们的个数。
find `echo $PATH|tr ":" " "` -type f -name "???"|awk '{cnt+=1;print $0} END{print "Total Count:",cnt}' >>command.txt
示例:1+2+...+100,加到100的值,用awk实现。
- awk '{if (a<100) {++a;b+=a;print a,b}}' /etc/services
- awk 'BEGIN {for (i=1;i<101;++i) {s+=i;print s}}'|tail -1
- echo |awk 'BEGIN {for (i=1;i<101;++i) {s+=i}} END{print s}'
2. awk数组
- 数组的索引不必是连续的设定的值;
- 可以使用字符串或数字作为数组的索引
- 不需要事先声明数组的大小
- 数组可以在运行时扩展/收缩
数组的语法:
array_name[index] = value
其中,array_name是数组的名称,index是数组索引,value是任意值分配给数组的元素。
2.1 创建 array_name[index] = value
[root@oldboy test]# awk 'BEGIN{fruits["mango"]="yellow";fruits["orange"]="orange";print fruits["orange"] " " fruits["mango"]}' orange yellow
2.2 删除 delete array_name[index]
使用delete语句从数组中删除元素。
[root@oldboy test]# awk 'BEGIN{fruits["mango"]="yellow";fruits["orange"]="orange";delete fruits["orange"];print fruits["mango"];print fruits["orange"]}' yellow [root@oldboy test]#
2.3 多维数组:用一维数组模拟多维数组
awk只支持一维数组,但是我们可以很容易地模拟使用一维阵列本身的多维阵列。
例如下面是3×3的三维阵列:
100 200 300 400 500 600 700 800 900
另外,在上述示例array[0] [0]存储100,array[0][1]存储200等。要在位置array[0] [0]存储100,我们可以使用下面的语法:
array["0,0"] = 100
虽然我们已经给定0,0为索引,这些都不是两个索引。在现实中,它只是一个索引以字符串0,0。
下面简单的例子模拟2-D数组:
awk 'BEGIN { array["0,0"] = 100; array["0,1"] = 200; array["0,2"] = 300; array["1,0"] = 400; array["1,1"] = 500; array["1,2"] = 600; # print array elements print "array[0,0] = " array["0,0"]; print "array[0,1] = " array["0,1"]; print "array[0,2] = " array["0,2"]; print "array[1,0] = " array["1,0"]; print "array[1,1] = " array["1,1"]; print "array[1,2] = " array["1,2"]; }'
在执行上面的代码后,得到以下结果:
array[0,0] = 100 array[0,1] = 200 array[0,2] = 300 array[1,0] = 400 array[1,1] = 500 array[1,2] = 600
我们还可以在排序其元素/索引于阵列执行各种操作。
为了达到这个目的,可以使用AWK的asort以及asorti函数。我们将看到在后面的章节这些函数的使用。
2.4 无序和有序的关联数组
awk中的数组都是关联数组,数字索引也会变成字符串索引:
[root@oldboy test]# awk 'BEGIN {cities[1]="beijing" > cities[2]="shanghai" > cities["three"]="guangzhou" > for (c in cities) {print cities[c]} > print cities[1] > print cities["1"] > print cities["three"] > }' guangzhou beijing shanghai beijing beijing guangzhou
for循环的输出,因为数组是关联数组,默认是无序的。通过for循环得到的是无序的数组。
如果需要得到有序数组,需要通过下标指定获得。
2.5 awk数组的典型应用
案例:用awk查看服务器连接状态并汇总
[root@oldboy test]# netstat -an Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 192.168.0.109:22 192.168.0.102:53242 ESTABLISHED tcp 0 0 :::80 :::* LISTEN tcp 0 0 :::22 :::* LISTEN udp 0 0 0.0.0.0:68 0.0.0.0:* Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node Path unix 2 [ ACC ] STREAM LISTENING 9885 /var/lib/mysql/mysql.sock unix 2 [ ACC ] STREAM LISTENING 7111 @/com/ubuntu/upstart unix 2 [ ] DGRAM 7512 @/org/kernel/udev/udevd unix 5 [ ] DGRAM 9369 /dev/log unix 2 [ ] DGRAM 209043 unix 2 [ ] DGRAM 20157 unix 2 [ ] DGRAM 9921 unix 3 [ ] DGRAM 7530 unix 3 [ ] DGRAM 7529 [root@oldboy test]# netstat -an|awk '/^tcp/{++s[$NF]} END{for (a in s) print a,s[a]}' ESTABLISHED 1 LISTEN 4
++s[$NF] 即将s作为数组的名称,索引是$NF,通过++s增量加1的方式,遇到一次s["Listen"]的值加1,非常的高校实用。
等价的表示方式为:
[root@oldboy test]# netstat -an|awk '/^tcp/{state[$NF]+=1;print s state[$NF] $NF} END{for (a in state) print a,state[a]}' 1LISTEN 2LISTEN 1ESTABLISHED 3LISTEN 4LISTEN ESTABLISHED 1 LISTEN 4
案例:统计web日志访问流量,要求输出访问次数,请求页面或图片,每个请求的总大小,总访问流量的大小汇总。
需要结合httpd服务的日志文件进行解析。
面试题:处理以下文件内容,将 域名计算计数 排序处理(去重)
http://www.etiantian.org/index.html http://www.etiantian.org/1.html http://post.etiantian.org/index.html http://mp3.etiantian.org/index.html http://www.etiantian.org/3.html
http://post.etiantian.org/2.html
答案:
[root@oldboy test]# awk -F "/" '{print $3}' html.txt |sort -rn|uniq -c 3 www.etiantian.org 2 post.etiantian.org 1 mp3.etiantian.org
[root@oldboy test]# awk -F "/" '{++domain[$3];print $3,domain[$3]}' html.txt www.etiantian.org 1 www.etiantian.org 2 post.etiantian.org 1 mp3.etiantian.org 1 www.etiantian.org 3 post.etiantian.org 2 [root@oldboy test]# awk -F "/" '{++domain[$3]} END{for (a in domain) print a,domain[a]}' html.txt mp3.etiantian.org 1 post.etiantian.org 2 www.etiantian.org 3
3. if语句
awk提供了类似C语言的if语句。
if ( $1== "foo" ) { if ( $2== "foo" ) { print "uno" } else print "one" } elseif ($1== "bar" ) print "two" } else print "three"
}
}
使用if语句还可以将代码:
! /match pattern/ {print $1}
转换成:
{ if ($0 !~/match pattern/) { print $1 } }
if语句,它只是测试条件,并执行特定操作,这取决于条件。
if语句语法:
if (conditon) action
if (condition) {action1 action2 action3 ... action-n}
if-else语句语法:
if (conditon) action-1 else action-2
if-else-if阶梯语法:
if (condition1) action-1 else if (condition2) action2 else if (condtiton3) action3
示例:检查给定的数是否为偶数。
[root@oldboy test]# awk 'BEGIN{num=10;if (num%2==0) printf "%d is even number. ",num}' 10 is even number.
[root@oldboy test]# awk 'BEGIN{num=10;if (num%2==0) printf "%d is even number. else",num; else printf "%d is odd number. ",num}' 10 is even number. root@oldboy test]# awk 'BEGIN{num=9;if (num%2==0) printf "%d is even number. else",num; else printf "%d is odd number. ",num}' 9 is odd number.
# if-else-if 示例:
[root@oldboy test]# awk 'BEGIN { > a=30; > if (a==10) > print "a=10"; > else if (a==20) > print "a=20"; > else if (a==30) > print "a=30"; > }' a=30
4. 循环结构:for循环,while循环,do-while循环
4.1 for循环
for循环语法:
for (initalisation; condition; increment/decrement) action
示例:
[root@oldboy test]# awk 'BEGIN{for (i=1;i<=5;++i) print i}' # 初始化动作i=1;条件i<=5;增量或减量 1 2 3 4 5
4.2 while循环
while循环语法:
while (condition) action
awk首先检查条件,如果条件为真,就执行操作,不断重复,只要循环条件满足为真。
[root@oldboy test]# awk 'BEGIN {i=1; while (i<6) {print i; ++i}}' 1 2 3 4 5
4.3 do-while循环
do-while循环语法:
do action while (condition)
do-while循环类似于while循环,不同之处在于测试条件是否在循环结束时进行计算。
do-while循环操作语句被执行至少一次,即使在条件语句的计算结果为假。
[root@oldboy test]# awk 'BEGIN{i=1; do {print i;++i} while (i<6)}' 1 2 3 4 5
4.4 break,continue,exit语句
- break 结束循环的执行
- continue 循环内跳到循环的下一次迭代
- exit 停止脚本的执行。接受一个整数作为参数,这将是awk进程的退出状态代码。如果没有任何参数,则返回状态为0.
[root@oldboy test]# awk 'BEGIN{sum=0; for (i=0;i<20;++i) {sum +=i; if (sum>50) break; else print "Sum =",sum}} ' Sum = 0 Sum = 1 Sum = 3 Sum = 6 Sum = 10 Sum = 15 Sum = 21 Sum = 28 Sum = 36 Sum = 45 [root@oldboy test]# awk 'BEGIN{for (i=1; i<=20; ++i) {if (i%2==0) print i; else continue}}' 2 4 6 8 10 12 14 16 18 20 [root@oldboy test]# awk 'BEGIN{sum=0; for (i=0;i<20;++i) {sum +=i; if (sum>50) exit(10); else print "Sum =",sum}}' Sum = 0 Sum = 1 Sum = 3 Sum = 6 Sum = 10 Sum = 15 Sum = 21 Sum = 28 Sum = 36 Sum = 45
[root@oldboy test]# echo $? # 检查脚本的返回状态
10