Linux-文本处理三剑客awk详解+企业真实案例(变量、正则、条件判断、循环、数组、分析日志)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 文本处理 awk1.awk简介awk是一种编程语言,用于在Linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其他命令的输出,它支持用户自定义函数和动态正则正则表达式等先进功能,是Linux/unix下的一个强大的编程工具。它在命令行中使用,但更多是作为脚本来使用。

文本处理 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关系运算符image.png

字符串建议用双引号引起来
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}'
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
15天前
|
存储 SQL 监控
|
15天前
|
运维 监控 安全
|
18天前
|
监控 关系型数据库 MySQL
分析慢查询日志
【10月更文挑战第29天】分析慢查询日志
35 3
|
18天前
|
监控 关系型数据库 数据库
怎样分析慢查询日志?
【10月更文挑战第29天】怎样分析慢查询日志?
32 2
|
16天前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
131 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
1月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
233 3
|
3月前
|
Kubernetes Ubuntu Windows
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
134 3
|
1月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1638 14
|
1月前
|
Python
log日志学习
【10月更文挑战第9天】 python处理log打印模块log的使用和介绍
35 0
|
1月前
|
数据可视化
Tensorboard可视化学习笔记(一):如何可视化通过网页查看log日志
关于如何使用TensorBoard进行数据可视化的教程,包括TensorBoard的安装、配置环境变量、将数据写入TensorBoard、启动TensorBoard以及如何通过网页查看日志文件。
201 0
下一篇
无影云桌面