awk是一种编程语言,用于linux/unix下对于文本和数据进行处理,数据可以来自于标准输入、一个或多个文件,或是其他命令的输出。
awk处理文本和数据的方式:逐行扫描文件,默认是从第一行到最后一行,寻找匹配的行,并执行相应的操作
awk可以用"统计数据",如网站的访问量、访问ip量…;支持条件判断;支持for和while循环;正则表达式。
awk有两种使用方式:命令行模式和脚本模式
语法:
awk 选项 ‘命令部分’ 文件名
如果要引用shell变量需用双引号引起
常用的选项介绍
-F 定义字段分割符号,默认的分隔符是"空格"
-v 定义变量并赋值
awk内置变量
awk中BEGIN…END使用
BEGIN:表示在程序开始前执行
END :表示所有文件处理完后执行
用法:'BEGIN{开始处理之前};{处理中};END{处理结束后}'
awk使用基础:
[root@local tmp]# cat a.txt 测试文件 root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin [root@local tmp]# awk 'NR==5' a.txt **输出a.txt的第5行** lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin [root@local tmp]# awk 'NR==5,NR==7' a.txt 输出a.txt的第5到7行 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown [root@local tmp]# awk '/root/' a.txt 过滤出包含root的行 root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@local tmp]# awk -F: 'NR==5{print $1,$NF}' a.txt 指定分隔符为:输出第5行第1个字段和最后一给字段的值 lp /sbin/nologin [root@local tmp]# awk -F: 'NR==5,NR==7{print $1,$NF}' a.txt 指定分隔符为:输出第5行到第7行第1个字段和最后一给字段的值 lp /sbin/nologin sync /bin/sync shutdown /sbin/shutdown [root@local tmp]# awk -F: '/root/{print $1,$NF}' a.txt 过滤出包含root行的第个字段和最后一个字段的值 root /bin/bash operator /sbin/nologin [root@local tmp]# awk -F: '/^root/;/^lp/{print $1,$NF}' a.txt 输出以root开头和以lp开头行的第1列和最后一列 root:x:0:0:root:/root:/bin/bash lp /sbin/nologin [root@local tmp]# awk -F: '/^root/,/^lp/{print $1,$NF}' a.txt 输出以root开头到以lp开头行的第1列和最后一列 root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin lp /sbin/nologin 内置变量分隔符举例(FS和OFS): [root@local tmp]# awk 'BEGIN{FS="/"};/^root/;/^lp/{print $1,$NF}' a.txt 指定分隔符为/,输出以root开头和以lp开头行的第1列和最后一列 root:x:0:0:root: bash lp:x:4:7:lp: nologin [root@local tmp]# awk 'BEGIN{FS=":"};/^root/;/^lp/{print $1,$NF}' a.txt 指定分隔符为/,输出以root开头和以lp开头行的第1列和最后一列。这种写发和awk -F: 结果式一样的 root /bin/bash lp /sbin/nologin awk -F: 'BEGIN{OFS="@@@"};/^root/,/^lp/{print $1,$NF}' a.txt 设置分隔符为:,输出的分隔符为@@,以root开头和以lp开头行的第1列和最后一列 root@@/bin/bash bin@@/sbin/nologin daemon@@/sbin/nologin adm@@/sbin/nologin lp@@/sbin/nologin
awk的使用进阶:
printf相当于echo -n %s 字符类型 strings %d 数值类型 - 表示左对齐,默认是右对齐 printf默认不会在行尾自动换行,加\n
演示:
[root@local tmp]# awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' a.txt root x 0 bin x 1 daemon x 2 adm x 3 lp x 4 sync x 5 shutdown x 6 halt x 7 mail x 8 operator x 11
awk变量定义:
awk -v 变量名=变量值 ‘awk语句’ 文件名
[root@local tmp]# awk -v num=5 'BEGIN{print num}' 设置num的值为5,并打印awk的值 5 [root@local tmp]# awk -v num=7 -F: '{print $num}' a.txt 设置num的值为7,并于$连用,表示第7列单元 /bin/bash /sbin/nologin /sbin/nologin /sbin/nologin /sbin/nologin /bin/sync /sbin/shutdown /sbin/halt /sbin/nologin /sbin/nologin
BEGIN…END…实例
#在awk语句执行之前打印,执行后在打印。 [root@local tmp]# awk -F: 'BEGIN{ print "Login_user\t\tLogin_shell\n****************************"};NR==1,NR==5{print $1"\t\t"$NF};END{print "****************************"}' a.txt Login_user Login_shell **************************** root /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin lp /sbin/nologin **************************** #相比较上面一个命令,结果格式化了输出 [root@local tmp]# awk -F: 'BEGIN{print"u_name\t\th_dir\t\tshell\n***************************"};NR==1,NR==5{printf "%-20s %-20s %-20s\n",$1,$(NF-1),$NF};END{print "****************************"}' a.txt u_name h_dir shell *************************** root /root /bin/bash bin /bin /sbin/nologin daemon /sbin /sbin/nologin adm /var/adm /sbin/nologin lp /var/spool/lpd /sbin/nologin ****************************
awk和正则的综合运用
运算符 说明 `== 等于 != 不等于 > 大于 < 小于 >= 大于等于 <= 小于等于 ~ 匹配 !~ 不匹配 ! 逻辑非 && 逻辑与 \|\| 逻辑或 `
实例
从第一行开始匹配到以lp开头行 awk -F: 'NR==1,/^lp/{print $0 }' a.txt 从第一行到第5行 awk -F: 'NR==1,NR==5{print $0 }' a.txt 从以lp开头的行匹配到第10行 awk -F: '/^lp/,NR==10{print $0 }' a.txt 从以root开头的行匹配到以lp开头的行 awk -F: '/^root/,/^lp/{print $0}' a.txt 打印以root开头或者以lp开头的行 awk -F: '/^root/ || /^lp/{print $0}' a.txt awk -F: '/^root/;/^lp/{print $0}' a.txt 显示5-10行 awk -F: 'NR>=5 && NR<=10 {print $0}' a.txt awk -F: 'NR<10 && NR>5 {print $0}' a.txt 打印1-9行以root开头的内容: awk 'NR>=1 && NR<=9 && $0 ~ /^root/{print $0}' a.txt awk 'NR>=1 && NR<=9 && /^root/' a.txt 显示可以登录系统的用户名 [root@local tmp]# awk -F: '$0 ~ /\/bin\/bash/{print $1}' /etc/passwd root yyy zzz 打印出系统中普通用户的UID和用户名 [root@local tmp]# awk -F: 'BEGIN{print "UID\tUSERNAME"} {if($3>=1000 && $3 !=65534 ) {print $3"\t"$1} }' /etc/passwd UID USERNAME 1001 yyy 1002 zzz
awk的脚本编程
㈠ 流程控制语句
① if结构
格式:
{ if(表达式){语句1;语句2;…}}
[root@local tmp]# awk -F: '{if($3>=1000 && $3<=60000) {print $1,$3} }' /etc/passwd yyy 1001 zzz 1002
② if…else结构
格式:
{if(表达式){语句;语句;…}else{语句;语句;…}}
通过if..else结构判断用户是否是普通用户 awk -F: '{ if($3>=500 && $3 != 65534) {print $1"是普通用户"} else {print $1,"不是普通用户"}}' b.txt
③ if…elif…else结构
格式:
{ if(表达式1){语句;语句;…}else if(表达式2){语句;语句;…}else if(表达式3){语句;语句;…}else{语句;语句;…}}
# if...elif...else判断用户的类型 awk -F: '{ if($3==0) {print $1,":是管理员"} else if($3>=1 && $3<=999 || $3==65534 ) {print $1,":是系统用户"} else {print $1,":是普通用户"}}' /etc/passwd #通过if...elif...else统计用户类型的个数 awk -F: '{ if($3==0) {i++} else if($3>=1 && $3<=999 || $3==65534 ) {j++} else {k++}};END{print "管理员个数为:"i "\n系统用户个数为:"j"\n普通用户的个数为:"k }' /etc/passwd awk -F: '{if($3==0) {i++} else if($3>=1 && $3<500 || $3==65534){j++} else {k++}};END{print "管理员个数为:" i RS "系统用户个数为:"j RS "普通用户的个数为:"k }' /etc/passwd
(二)、 循环语句
① for循环
#打印1~5 awk 'BEGIN { for(i=1;i<=5;i++) {print i} }' for ((i=1;i<=5;i++));do echo $i;done|awk '{print $0}' awk 'BEGIN{ for(i=1;i<=5;i++) {print i} }' awk 'BEGIN{ for(i=1;i<=5;i++) print i }' 计算1-5的和 awk 'BEGIN{sum=0;for(i=1;i<=5;i++) sum+=i;print sum}' awk 'BEGIN{for(i=1;i<=5;i++) (sum+=i);{print sum}}' awk 'BEGIN{for(i=1;i<=5;i++) (sum+=i);print sum}'
② while循环
#打印1~5 i=1;while (($i<=5));do echo $i;let i++;done awk 'BEGIN { i=1;while(i<=5) {print i;i++} }' awk 'BEGIN{i=1;while(i<=5) {print i;i++} }' 计算1-5的和 awk 'BEGIN{i=1;sum=0;while(i<=5) {sum+=i;i++}; print sum }' awk 'BEGIN {i=1;while(i<=5) {(sum+=i) i++};print sum }'
③ 嵌套循环
普通的shell脚本实现 #!/bin/bash for ((y=1;y<=5;y++)) do for ((x=1;x<=$y;x++)) do echo -n $x done echo done 使用awk实现 awk 'BEGIN{ for(y=1;y<=5;y++) {for(x=1;x<=y;x++) {printf x} ;print } }' awk 'BEGIN{ y=1;while(y<=5) { for(x=1;x<=y;x++) {printf x};y++;print}}'
break 条件满足的时候跳出循环
continue 条件满足的时候跳过当次循环
awk统计案例
#统计/etc/passwd中各种bash的数量 awk -F: '{ shells[$NF]++ };END{for (i in shells) {print i,shells[i]} }' /etc/passwd #统计网站访问状态 ss -antp|grep 80|awk '{states[$1]++};END{for(i in states){print i,states[i]}}' 统计访问网站的每个IP的数量 netstat -ant |grep :80 |awk -F: '{ip_count[$8]++};END{for(i in ip_count){print i,ip_count[i]} }' |sort ss -an |grep :80 |awk -F":" '!/LISTEN/{ip_count[$(NF-1)]++};END{for(i in ip_count){print i,ip_count[i]}}' |sort -k2 -rn |head 统计网站日志中PV量 统计Apache/Nginx日志中某一天的PV量 grep '27/Jul/2017' mysqladmin.cc-access_log |wc -l 统计Apache/Nginx日志中某一天不同IP的访问量 grep '27/Jul/2021' mysqladmin.cc-access_log |awk '{ips[$1]++};END{for(i in ips){print i,ips[i]} }' |sort -k2 -rn |head grep '27/Jul/2021' access.log |awk '{ips[$1]++};END{for(i in ips){print i,ips[i]} }' |awk '$2>100' |sort -k2 -rn