文本处理 awk
1.awk简介
awk是一种编程语言,用于在Linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其他命令的输出,它支持用户自定义函数和动态正则正则表达式等先进功能,是Linux/unix下的一个强大的编程工具。它在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它逐行扫描文件从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作,如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。
2.awk的两种命令格式
awk [options] ‘commands’ filenames
awk [options] -f awk-scripts-file filenames
=options:===
-F 定义输入字段分隔符,默认的分隔符是空格或制表符
==command:
BEGIN{} {} END{}
行处理前 行处理 行处理后
awk 'BEGIN{print 1/2}{print "ok"}END{print "-----------"}' /etc/hosts 0.5 ok ok ok --------- 在行处理前执行1/2,在处理每一行后打印ok,在行处理后在打印--------,整个命令的原理:先进行行前处理,也就是打印出1/2的值也就是0.5,然后根据awk的原理,不指定处理哪一行默认都进行处理,因此每当处理一行后都会打印ok,最后在每一行都处理结束后打印-------------
BEGIN{}通常用于定义一些变量
awk 'BEGIN{FS=":";OFS="---"}{print $1,$2}' /etc/passwd //在BEGIN中定义FS的值以:进行分隔,FS表示输入时分隔符为xxx,
3.awk命令格式:
awk ‘pattern’ filename
示例
awk -F: '/root/' /etc/passwd 过滤出包含root的行
awk ‘{action}’ filename
示例
awk -F ":" '{print $1}' /etc/passwd 以分号分隔,打印出第一列
awk ‘pattern {action}’ filename
示例
awk -F ":" '/root/{print $1,$2}' /etc/passwd 以分号作为分隔符,打印出包含root的行并打印第一列 awk 'BEGIN{FS=":";OFS="---"} /root/{print $1,$2,$3}' passwd.txt 以BEGIN行前处理定义FS的分隔符,OFS定义输出分隔符,匹配包含root的行并打印1,2,3列 df | grep '/' | awk '$4>100000 {print $4,$(NF)}' df命令列出磁盘空间后,使用grep命令过滤/的行,在使用awk过滤一下$4大于100000的行在打印第4列和最后一列
4.awk工作原理
awk -F: '{print $1,$2,$3}' /etc/passwd 1.awk使用一行作为输入,并将这然后一行赋给内部编码$0,每一行也可称为一个记录,以换行符结束 2.然后,行被:(默认为空格或者制表符,但是由于我们使用了-F也就是重新定义了分隔符,因此在这里为:)分解成字段(或域),每个字段存储在已经编号的变量中,从$1开始,最多达100个字段 3.awk如何知道用空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格 4.awk打印字段时,将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为$1,$3之间有一个逗号,逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格 5.awk输出后,将文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理,该过程将持续到所有行处理完毕
5.awk记录与字段相关内部变量
man awk可以看到所有的awk帮助信息
$0:awk变量$0保存当前记录的内容
awk -F":" '{print $0}' passwd.txt
NR:The total number of input records seen so far
显示行号总数,例如有两个文件同时输出到屏幕,行号会接着之前文件的继续加之,NR是总的
awk -F":" '{print NR,$0}' passwd.txt /etc/hosts 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin 4 adm:x:3:4:adm:/var/adm:/sbin/nologin 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 6 sync:x:5:0:sync:/sbin:/bin/sync 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 8 halt:x:7:0:halt:/sbin:/sbin/halt 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin 10 operator:x:11:0:operator:/root:/sbin/nologin 11 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 12 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 如果把NR放在了后面则在行尾加上行号 awk -F":" '{print $0,NR}' passwd.txt /etc/hosts root:x:0:0:root:/root:/bin/bash 1 bin:x:1:1:bin:/bin:/sbin/nologin 2 daemon:x:2:2:daemon:/sbin:/sbin/nologin 3 adm:x:3:4:adm:/var/adm:/sbin/nologin 4 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 5 sync:x:5:0:sync:/sbin:/bin/sync 6 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 7 halt:x:7:0:halt:/sbin:/sbin/halt 8 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin 9 operator:x:11:0:operator:/root:/sbin/nologin 10 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 11 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 12 awk也可以结合sed命令来使用,sed命令不输出行号,所以可以结合awk来实现 awk -F":" '{print NR,$0}' passwd.txt |sed '1,3s/.*/#&/'
FNR:The input record number in the current input file
FNR的不像NR多个文件的行数都写在一块,FNR是当前输入文件的行号
awk -F":" '{print FNR,$0}' passwd.txt /etc/hosts 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin 4 adm:x:3:4:adm:/var/adm:/sbin/nologin 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 6 sync:x:5:0:sync:/sbin:/bin/sync 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 8 halt:x:7:0:halt:/sbin:/sbin/halt 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin 10 operator:x:11:0:operator:/root:/sbin/nologin 1 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 2 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
NF:保存记录的字段数,$1,$2,$3的个数
一般都会把NF放在最后,审美
NF与N F 的 区 别 : N F 是 打 印 字 段 的 个 数 , NF的区别:NF是打印字段的个数,NF的区别:NF是打印字段的个数,NF是打印最后一列
awk -F":" '{print NR,$0,NF}' passwd.txt 1 root:x:0:0:root:/root:/bin/bash 7 2 bin:x:1:1:bin:/bin:/sbin/nologin 7 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin 7 4 adm:x:3:4:adm:/var/adm:/sbin/nologin 7 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 7 6 sync:x:5:0:sync:/sbin:/bin/sync 7 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 7 8 halt:x:7:0:halt:/sbin:/sbin/halt 7 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin 7 10 operator:x:11:0:operator:/root:/sbin/nologin 7 awk -F":" '{print NR,$0,NF,$NF}' passwd.txt 1 root:x:0:0:root:/root:/bin/bash 7 /bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin 7 /sbin/nologin 3 daemon:x:2:2:daemon:/sbin:/sbin/nologin 7 /sbin/nologin 4 adm:x:3:4:adm:/var/adm:/sbin/nologin 7 /sbin/nologin 5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 7 /sbin/nologin 6 sync:x:5:0:sync:/sbin:/bin/sync 7 /bin/sync 7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 7 /sbin/shutdown 8 halt:x:7:0:halt:/sbin:/sbin/halt 7 /sbin/halt 9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin 7 /sbin/nologin 10 operator:x:11:0:operator:/root:/sbin/nologin 7 /sbin/nologin
FS:输入字段分隔符,默认空格
awk -F":" '{print $1}' passwd.txt /etc/hosts root bin daemon adm lp sync shutdown halt mail operator 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 多重匹配分隔符[ :\t]表示以空格冒号制表符作为分隔符,出现对应的符号就进行分隔 awk -F"[ :\t]" '{print $1,$2,$3,$4}' passwd.txt /etc/hosts root x 0 0 bin x 1 1 daemon x 2 2 adm x 3 4 lp x 4 7 sync x 5 0 shutdown x 6 0 halt x 7 0 mail x 8 12 operator x 11 0 127.0.0.1 localhost 1 也可以使用BEGIN作为分隔符 awk 'BEGIN{FS=":"} {print $1}' passwd.txt awk -F: '/root/{print $1,$2,$3,$4}' passwd.txt
OFS:输出字段分隔符
awk 'BEGIN{FS=":";OFS="+++"}/root/{print $1,$2,$3}' passwd.txt
RS:输入记录分隔符,可以将一行换成多行
以冒号分隔 awk 'BEGIN{RS=":"}{print $0}' passwd.txt 以空格分隔 awk 'BEGIN{RS=" "}{print $0}' passwd.txt
ORS:输出记录分隔符,可以将多行合并成一行
awk 'BEGIN{ORS=""}{print $0}' passwd.txt awk 'BEGIN{ORS=" "}{print $0}' passwd.txt
字段分隔符:FS OFS 默认空格或者制表符
记录分隔符:RS ORS 默然换行符
6.格式化输出
print函数
使用print函数时打印非变量的字符时一定要用引号引起来
输出当前的月份和年份 date | awk '{print "month: "$2 "\nyear: "$1}' month:03月 year: 2020年 输出passwd.txt文件中的用户名和uid awk -F":" '{print "username is: "$1 "\tuid is: "$3}' passwd.txt awk -F":" '{print "username and uid: "$1,$3}' passwd.txt
printf 函数
awk -F ":" '{printf "%-15s %-10s %-15s\n",$1,$2,$3}' passwd.txt //设置字符长度 awk -F ":" '{printf "%-15s| %-10s| %-15s\n",$1,$2,$3}' passwd.txt awk -F ":" '{printf "%-25s| %-20s| %-25s|\n","username:"$1,"userpass:"$2,"useruid:"$3}' passwd.txt %s字符类型 %d数值类型 %f浮点类型 占15个字符 -表示左对齐,默认右对齐 printf默认不会再行尾自动换行,加\n
7.模式匹配
7.1正则表达式:
匹配记录(整行):
awk '/^root/' passwd.txt awk '$0 ~ /^root/' passwd.txt //$0匹配root开头的行,~表示匹配 awk '!/^root/' passwd.txt //除了root的行 awk '$0 !~ /^root/' passwd.txt //$
匹配字段:匹配操作符(~ !~)
awk -F":" '$1 ~ /^root/' /etc/passwd //匹配 awk -F":" '$NF !~ /bash$/' /etc/passwd //非匹配
7.2关系运算符
字符串建议用双引号引起来 awk -F ":" '$3 == 0' /etc/passwd //$3等于0 awk -F ":" '$3 < 10' /etc/passwd //$3小于10 awk -F ":" '$NF == "/bin/bash"' /etc/passwd //最后一列等于/bin/bash awk -F ":" '$1 == "root"' /etc/passwd //$1等于root awk -F ":" '$1 ~ /root/' /etc/passwd //$1匹配正则root的行 awk -F ":" '$1 !~ /root/' /etc/passwd //$1不匹配root,就是不包含 df | grep '/' |awk '$4 < 1000'
7.3条件表达式:
awk -F":" '$3>300 {print $0}' /etc/passwd //如果$3这一列大于300那么久打印整行 awk -F":" '{if($3>300) print $0}' /etc/passwd//如果$3这一列大于300那么久打印整行 awk -F":" '{if($3>300){print $3}else{print $1}}' /etc/passwd
7.4算术运算:+ - * / % ^
awk -F":" '{if($3 * 5 > 10){print $1,$3}}' passwd.txt //如果$3*5的结果大于10,那么就打印$1,$3 awk -F":" '{if($3 + 5 > 10){print $1,$3}}' passwd.txt awk -F":" '{if($3 - 5 < 10){print $1,$3}}' passwd.txt awk -F":" '{if($3 / 5 < 10){print $1,$3}}' passwd.txt awk -F":" '{if($3^5 > 10){print $1,$3}}' passwd.txt
7.5逻辑操作符和复合模式
&& 逻辑与 a&&b
|| 逻辑或 a||b
! 逻辑非 !a
awk -F ":" '$1~/root/ && $3<=15' /etc/passwd awk -F ":" '$1~/root/ || $3<=15' /etc/passwd awk -F ":" '!($1~/root/ || $3<=15)' /etc/passwd
8.awk示例
awk '/root/' passwd.txt //匹配root的行 awk '/^root/' passwd.txt //匹配以root开头的行 awk '$3 ~ /^root/ ' passwd.txt //$3匹配root开头 awk '/^(no|so)/' passwd.txt //匹配no或者so开头的行 awk '{print $3,$2} ' passwd.txt //打印第三列和第二列 awk '{print $0}' passwd.txt //打印整行 awk '{print "Number of fields" NF}' passwd.txt //打印每一行的字段数 awk '/root/{print $3,$2}' file //匹配root的行并打印$3和$2两列 awk '/E/' passwd.txt //匹配E的行 awk '/^[ns]/{print $1}' datafile //匹配以n或s开头的行并打印$1 awk '$5 ~ /\.[7-9]+' datafile //$5匹配.开头在跟一个7-9出现一次或多次 awk awk '$4 ~ /China$/{print $8}' datafile //$4匹配china结尾的并打印$8 awk '/Tj/{print $0}' datafile //匹配Tj的行并打印 awk '{print $1}' /etc/passwd awk -F ":" '{print $1}' /etc/passwd awk -F ":" '{print "Number of felds: "NF}' /etc/passwd awk -F"[ :]" '{print NF}' /etc/passwd //指定多个分隔符,空格或者冒号都进行分隔 awk -F"[ :]+" '{print NF}' /etc/passwd //空格或者分隔符出现一次或多次 awk '$7 == 5' datafile //$7等于5 awk '$2 == "CT"{print $1,$2}' datafile //$2等于CT的行并打印$1和$2 awk '$7 != 5' datafile //$7不等于5 awk '$7 < 5 {print $4,$7}' datafile //$7小于5打印第四列和第七列 awk '$6 > .9{print $1,$6}' datafile //$6大于0.9则打印第一列和第六列 awk '$8 <= 17 {print $8}' datafile //$8下雨等于17则打印第八列 awk '$8 > 10 && $8 <17' datafile //$8大于10并且$8小于17 综上可以使用if的方式:awk '{if($7 != 5){print $1,$2}}' datafile awk '$2 == "NW" || $1 ~ /south/ {print $1,$2}' datafile //$2等于NW或者$1匹配south的行然后打印$1和$2 awk '!($8 == 13){print $8}' datafile //如果$8不等于13就打印$8 awk '/southem/{print $5 + 10}' datafile //匹配suouthem并打印$5+10 awk '/southem/{print $8 - 10}' datafile //$8减10 awk '/southem/{print $8 / 2}' datafile //$8除于2 awk '/southem/{print $8 * 2}' datafile //$8乘2 awk '/southem/{print $8 % 2}' datafile //$8余数2 awk '$3 ~ /^Suan/ {print "Percentage:"$6 + .2 "volume: "$8}' //$3匹配Suan开头的行,打印$6+0.2并打印$8 awk '/^western/,'/^eastern/' datafile awk '{print ($7 > 4 ? "high: "$7: "low" $7)}' datafile //如果$7大于4则打印$7否则打印$7可以使用if else实现 awk '$3 == "Chirs"{$3 = "Chiristian"; print $0}' datafile //$3等于chirs则将$3赋值chiristian 并打印$0 awk '/Derek/ {$8+=12;print $8}' datafile //$8=$8+12 awk '{$7%=3; print $7}' datafile //$7=$7 % 3
9.awk脚本编程
表达式用(),命令语句用{}
9.1.条件判断
if语句: 格式:{if(表达式){语句1,语句2}} awk -F ":" '{if($3 == 0){print $1 "is administrator"}}' /etc/passwd //如果$3=0就打印$1是管理员 awk -F ":" '{if($3>0 && $3<1000){count++;}} END{print count}' /etc/passwd.txt //如果$3大于0并且$3大于1000那么count++也就是每当条件成立一次,count的值就会加1,最后在打印cront的值,如果想加一次都显示值则在{count++;print count}即可 ps -ef | awk '{if($1 ~ /root/ ){i++}} END{print "root用户开启的进程个数为: "i}' //打印root用户开启了多少个进程 if..else语句: 格式:{if(表达式){语句;语句;...}else{语句;语句;...}} awk -F ":" '{if($3==0){i++} else{j++}} END{print "管理员个数: "i;print "系统用户个数: "j}' /etc/passwd //如果$3=0则i的值每次加1,否则j每次加1, awk -F ":" '{if($3==0){i++}else{j++}} END{print "管理员个数: "i "\n系统用户个数: "j}' /etc/passwd //可以使用一个print,并使用\n进行还行 if...else if...else语句 格式:'{if(表达式1){语句;语句}else if(表达式2){语句;语句} else if(表达式3){语句;语句} else{语句}}' awk -F":" '{if($3==0){i++} else if($3>999){j++} else{s++}} END{print i;print j;print s}' /etc/passwd awk -F":" '{if($3==0){i++} else if($3<999){j++} else{s++}} END{print "管理员个数: "i;print "程序用户个数: "j;print "普通用户个数: "s}' /etc/passwd //如果$3=0则i的值每次都加1,如果$3小于999则j的值每次都加1,如果都不匹配则s的值每次加1,管理员的个数匹配i,程序用户个数匹配j,普通用户个数匹配s
9.2.循环
在awk中while循环和for循环的区别在于:while是将初值、累加分开写的如{i=1;while(i<=10){print $i;i++}}而for是都写在一起的,和shell中的for格式相同如{for(i=1;i<=10;i++){print $i}}
while循环
awk 'BEGIN{i=1;while(i<=10){print i; i++}}' //行前处理,先赋初值i=1,当i的值小于等于10的时候打印i的值,每次循环i的值加1,print i后面必须加分号,不然会打印两次 awk -F":" '{i=1;while(i<=NF){print $i; i++}}' passwd.txt //符初值,如果i小于等于字段个数,则打印$i列也就是第一次$1第一列,第二次$2第二列每次循环玩i的值都加1 awk -F":" '/^root/{i=1;while(i<=NF){print $i; i++}}' passwd.txt //匹配到root开头的行,然后进行循环 root1 x2 03 04 root5 /root6 /bin/bash7 awk -F ":" '/^root/{i=1;while(i<=7){print $i;i++}}' passwd.txt awk -F '{i=1;while(i<=NF){print $i;i++}}' b.txt //分别打印每行每列
9.3.for循环
awk 'BEGIN{for(i=1;i<=10;i++){print i}}' //跟shell中的for循环一样 awk -F":" '{for(i=1;i<=NF;i++){print $i}}' passwd.txt awk -F":" '/root/{for(i=1;i<=NF;i++){print $i}}' passwd.txt
9.4.数组
注意:遍历时,i in user是索引不是数组值,print i,state和print i;state不一样其中“,”表示在同一行显示,";"表示换行,需要增加print
格式:awk ‘{数组[索引]++} END{for(i in 数组){print i,数组[i]}}’
sort -k表示按第几列排序 -n 排序,-r表示逆序,就是降序
awk -F":" '{username[++i]=$1} END{print username[1]}' passwd.txt //定义一个数组,数组的索引是++i也就是第一次索引位1,第二次索引位2,数组的元数每次都是该行的第一列,最后打印数组 awk -F":" '{username[++i]=$1} END{print username[2]}' passwd.txt awk -F":" '{username[i++]=$1} END{print username[0]}' passwd.txt 数组遍历: netstat -ant | awk '{state[++i]=$NF} END{for (j in state){print j,state[j]}}' | sort -k1 -n //将最后一列作为数组的值,最后遍历索引,打印数组值,在用sort排序小,-k表示对第几列进行排序 awk -F":" '{username[++j]=$1} END{for(i in username){print i,username[i]}}' passwd.txt 练习 awk -F":" '{shells[$NF]++} END{for(i in shells){print i,shells[i]}}' /etc/passwd //统计各种登录shell出现的次数,这里使用shells[$NF]++而不是shells[++i]=$NF是因为我们要统计这一列出现的次数,将每一行的这一列都做成索引 统计网站访问状态 netstat -n | awk '/^tcp/{state[$NF]++} END{for(i in state){print i,state[i]}}' //导出系统并发数,以tcp开头的行最后一列作为数组索引,每当出现一次值就加1,最后遍历,等同于netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}',二者不同在于数组定义方面,后者是在数组名前就进行了++而前者是在索引定义好后++,效果一致。 netstat -ant | grep ":888" | awk '{state[$NF]++} END{for (i in state){print i,state[i]}}' | sort -k2 -n | head //将结果排序 ss命令实现 ss -an | grep ":888" |awk '{state[$2]++} END{for (i in state){print i,state[i]}}' ss -an | grep ":888" |awk '{state[$2]++} END{for (i in state){print i,state[i]}}' | sort -k2 -n | head 统计访问ip的次数 netstat -ant | grep ":888" | awk -F"[ :\t]+" '{ips[$6]++} END{for (i in ips){print i,ips[i]}}' ss -ant ss -ant | grep ":888" | awk -F"[ :\t]+" '{ips[$6]++} END{for (i in ips){print i,ips[i]}}' 统计ip时去掉listen ss -ant | grep ":888" |awk -F"[: \t]+" '!/LISTEN/{ips[$(NF-2)]++} END{for (i in ips){print i,ips[i]}}' | sort -k2 -nr netstat -ant | grep ":888" | awk -F"[: \t]+" '!/LISTEN/{ips[$(NF-3)]++} END{for (i in ips){print i,ips[i]}}' | sort -k2 -nr
9.5.分析nginx、Apache日志
统计一天内访问量
grep '08/Mar/2020' access.log|wc -l
统计日志中ip的访问次数
日志输出一般为下图
192.168.81.43 - - [08/Mar/2020:16:56:50 +0800] “GET /favicon.ico HTTP/1.1” 404 570 “http://192.168.81.250:888/know_system/” “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36”
awk ‘{ips[$1]++} END{for (i in ips){print i,ips[i]}}’
解释:由于我们要统计整个文件中每一行$1列的内容出现的次数,每出现一次,值就加一,因此我们在定义数组时,就把$1的内容作为索引,每出现一次就加1,ips[$1]++,最后在遍历这个数组,打印索引后面跟着索引的值,索引就是$1的内容,值就是$1出现了多少次
实现思路:将需要统计的内容(某一字段)作为数组索引,每次加1 如果需要判断出现的次数,也看结合之前学过的if进行判断,但要写在遍历完数组的部分,例如{if(ips[i]>100){print i,ips[i]}} 1.使用grep实现 grep '08/Mar/2020' access.log | awk '{ips[$1]++} END{for (i in ips){print i,ips[i]}}' | sort -k2 -nr 192.168.81.5 25 192.168.81.210 87 192.168.81.134 987 192.168.81.14 765 192.168.81.32 19 192.168.81.99 17 192.168.81.172 176 192.168.81.43 78 192.168.81.1 346 192.1.32.8 1790 192.168.81.2 99 2.awk实现 awk '/08\/Mar\/2020/{ips[$1]++} END{for (i in ips){print i,ips[i]}}' access.log | sort -k2 -nr 192.1.32.8 1790 192.168.81.134 987 192.168.81.14 765 192.168.81.1 346 192.168.81.172 176 192.168.81.2 99 192.168.81.210 87 192.168.81.43 78 192.168.81.5 25 192.168.81.32 19 3.只输出日志中访问次数最多ip的前五个 awk '/08\/Mar\/2020/{ips[$1]++} END{for (i in ips){print i,ips[i]}}' access.log | sort -k2 -nr | head -5 192.1.32.8 1790 192.168.81.134 987 192.168.81.14 765 192.168.81.1 346 192.168.81.172 176 4.只输出日志中访问次数超过100的ip awk '/08\/Mar\/2020/{ips[$1]++} END{for(i in ips){if(ips[i]>100){print i,ips[i]}}}' access.log | sort -k2 -nr 92.1.32.8 1790 192.168.81.134 987 192.168.81.14 765 192.168.81.1 346 192.168.81.172 176 awk '/08\/Mar\/2020/{ips[$1]++} END{for(i in ips){print i,ips[i]}}' access.log | awk '$2>100' | sort -k2 -nr 192.1.32.8 1790 192.168.81.134 987 192.168.81.14 765 192.168.81.1 346 192.168.81.172 176
9.6…awk函数
统计用户名为4个字符的用户
awk -F":" '$1~/^....$/{i++;print $1} END{print "count is "i}' /etc/passwd //利用正则4个.表示四个任意子,最后打印 awk -F":" '{if($1~/^....$/){i++;print $1}} END{print "count is "i}' /etc/passwd length函数实现 awk -F":" 'length($1)==4{i++;print $1} END{print "count is "i}' /etc/passwd awk -F":" '{if(length($1)==4){i++;print $1}} END{print "count is "i}' /etc/passwd
9.7.awk引入外部变量
首先定义外部变量:var=bash gsub是全局替换,sub是单个替换 注意:不能使用单引号,gsub/sub后面要有括号 何时用不同的引号:在函数中尽量使用双引号,在函数外面可用使用单引号,'''$i''' 方法一:在双引号情况下使用 var=bash echo "unix scripts" | awk "gsub(/unix/,\"$var\")" //全局替换 bash scripts echo "unix scripts unix unix" | awk "sub(/unix/,\"$var\")" //只替换一次 bash scripts unix unix echo "unix scripts unix unix" | awk "gsub(/unix/,\"$var\")" bash scripts bash bash 方法二:使用单引号的情况 可以使用两个双引号然后里面套入单引号 var=bash echo "unix scripts unix unix" | awk 'gsub(/unix/,"'"$var"'")' 函数外使用单引号 [root@localhost d11_awk_wbclgjsz]# df -hT 文件系统 类型 容量 已用 可用 已用% 挂载点 /dev/mapper/centos-root xfs 47G 7.6G 40G 17% / devtmpfs devtmpfs 894M 0 894M 0% /dev tmpfs tmpfs 910M 0 910M 0% /dev/shm tmpfs tmpfs 910M 11M 900M 2% /run tmpfs tmpfs 910M 0 910M 0% /sys/fs/cgroup /dev/sr0 iso9660 4.3G 4.3G 0 100% /media /dev/sdb1 xfs 100G 39M 100G 1% /my_scripts /dev/sda1 xfs 1014M 180M 835M 18% /boot tmpfs tmpfs 182M 0 182M 0% /run/user/0 tmpfs tmpfs 182M 12K 182M 1% /run/user/42 [root@localhost d11_awk_wbclgjsz]# df -h | awk '{if($(NF-1)>10){print $NF":"$(NF-1)}}' 挂载点:已用% /:17% /run:2% /media:100% /boot:18% 显然结果是不对的,因为倒数第二列有百分号,因此不能计算,我们可以使用int函数,将函数部分引起来int($5)>10 [root@localhost d11_awk_wbclgjsz]# df -h | awk '{if (int($(NF-1))>10){print $NF":"$(NF-1)}}' 下面开始进入正题,引入外部变量 i=10 df -h |awk '{if(int($5)>'''$i'''){print $6":"$5}}' 方法三:使用awk参数-v将外部变量引入 使用这种方法不需要在使用引号和$符号 echo "unix scripts unix aaa" | awk -v "var=bash" 'gsub(/unix/,var)' awk -v "user=root" -F":" '{if($1==user){print $0}}' /etc/passwd
10.awk企业真实实例
10.1过滤出网卡中的所有ip,不包含ipv6的地址
思路:首先ip在inet的行,正好是第二列,因此不必使用grep,直接使用awk '/inet/'即可匹配到包含inet的行,我们不要ipv6的地址,因此可以使用正则匹配,也就是$2第二列的值要匹配192.168.81.250这样的值也就是$2~/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/如果匹配成功就打印第二列 ifconfig | awk '/inet/{if($2~/([0-9]{1,3}.){3}[0-9]{1,3}/){print $2}}' 192.168.81.250 127.0.0.1 192.168.122.1 ifconfig |awk '/inet/{if($2~/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/){print $2}}' 192.168.81.250 127.0.0.1 192.168.122.1 因为包含ipv4的行时inet看开头的,inet6是ipv6的因此使用词首词尾定界符即可 ifconfig | awk '/\<inet\>/{print $2}'
10.2获取内存使用情况,如果超过80就提示error,否则就提示ok
思路:free -m查出来的结果第三列是使用的,第二列是总大小,使用的除以总大小乘以100就是磁盘使用情况,一定要主要是Mem开头的,因为我们要差的是内存,最后如果大于等于80就报错,否则就正常 free -m | awk '/^Mem/{if($3 / $2 *100 >= 80){print "free is error"} else{print "free is ok"}}' free is ok free -m | awk '/^Mem/{if($3 / $2 *100 >= 10){print "free is error"} else{print "free is ok"}}' free is error
10.3获取磁盘使用情况
思路:定义一个外部变量,只打印使用超过10%的,由于第第五列包含%因此我们使用int函数只比较数值 df -h | awk -v "used=10" '{if(int($5)>used){print $6":"$5}}' /:17% /media:100% /boot:18%
10.4清空本机arp缓存
arp -n //查看arp缓存 Address HWtype HWaddress Flags Mask Iface 192.168.81.2 ether 00:50:56:fb:50:ed C ens33 192.168.81.1 ether 00:50:56:c0:00:08 C ens33 arp -n | awk '/^[0-9]/{print "arp -d "$1}' //只是把清arp缓存的命令打印出来 arp -d 192.168.81.2 arp -d 192.168.81.1 arp -n | awk '/^[0-9]/{print "arp -d "$1}' | bash //也可以将打印结果交给bash来处理,相当于在bash执行输出的结果 arp -n | awk '/^[0-9]/{print $1}' | xargs -I {} arp -d {} //也可以只打印$1然后将输出的结果通过xargs -I保存到{}中,然后使用arp -d {}获取{}中的东西然后清理掉,xargs -I {}中的{}相当于接收在传给arp -d {} xargs -I {} 同样适用于导出线程 ps aux | grep 'java' | grep -v 'grep'| awk '{print $2}' | xargs -I {} jstack -l {}
10.5打印出/etc/hostswe你按中最后一个字段
awk '{print $NF}' /etc/hosts
10.6打印目录下面文件夹的名称
ll /my_scripts/ | awk '/^d/{print $NF}'