awk命令

一、awk简介

awk是一种编程语言,用于linux/unix下对于文本和数据进行处理,数据可以来自于标准输入、一个或多个文件,或是其他命令的输出。
awk处理文本和数据的方式:逐行扫描文件,默认是从第一行到最后一行,寻找匹配的行,并执行相应的操作
awk可以用"统计数据",如网站的访问量、访问ip量…;支持条件判断;支持for和while循环;正则表达式。

二、使用方式

awk有两种使用方式:命令行模式和脚本模式

命令行模式

语法:
awk 选项 ‘命令部分’ 文件名
如果要引用shell变量需用双引号引起

常用的选项介绍
-F 定义字段分割符号,默认的分隔符是"空格"
-v 定义变量并赋值

awk内置变量
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
上一篇:ByteCTF2021 double sqli


下一篇:shell数据筛选