AWK简介
内置变量
第四点中的逗号经常使用却不知其含义.
案例联系:
以空格冒号分隔出现一次或多次视为一个field
[root@ninesun example]# ifconfig docker0 | grep inet6 inet6 fe80::42:eeff:fe0b:8f3a prefixlen 64 scopeid 0x20<link> [root@ninesun example]# [root@ninesun example]# ifconfig docker0 | grep inet6 | awk -F"[ :]+" '{print $NF}' 0x20<link> [root@ninesun example]# [root@ninesun example]# ifconfig docker0 | grep inet6 | awk -F"[ :]+" '{print NF}' 11 [root@ninesun example]# [root@ninesun example]# [root@ninesun example]# ifconfig docker0 | grep inet6 | awk -F"[ :]" '{print NF}' 21
计算系统用户的uid sum值。(uid >0 uid< 1000)
1. [root@ninesun example]# awk -F":" '{ if($3 >0 && $3 <1000) {count++} } END {print count}' /etc/passwd 2. 25
以此打印每一列,print 后自带换行,printf不换行。
--update 2022-8-8 10:27:39 如何理解这个例子呢?
将i 和NF都打印出来,可以看出每次NF会从新行重新计算。
/tmp]$awk '{i=1;while(i<=NF) { print $i,i,NF;i++;} }' test1.txt 000 1 1 111 1 2 222 2 2 333 1 3 444 2 3 555 3 3 666 1 4 777 2 4 888 3 4 999 4 4 /tmp]$cat test1.txt | awk '{print NF}' 1 2 3 4
行处理中需要按照哪个filed分区,数组下标就是那个字段。 行处理后 for i in 数组。
统计Nginx日志
一、awk过滤某行某列值为 ** 的行
在监控gp的conn 连接情况时,有一个需求: 把某个用户的top -c的信息定向输出到文件,刚开始直接全部输出,可是数据量太大,有太多idle的conn,因此需要过滤进程在运行队列的top -c信息。
需求: 过滤Runnig 的top -c信息。
-b 指定批处理模式
-n 指定迭代次数
tee读取标准输入的数据并将其内容输出成文件 -a 代表append,每5 s收集一次
$8~/^R$/ 其中 ~匹配,与==相比不是精确比较 ,^hR$ 表示开始和结束都是R,代表状态是running的进程
再例如
/[0-9][0-9]*/ 一个或一个以上数字
/[0-9][0-9]+/ 两个或两个以上数字
#!/bin/bash # @date 2020 05 26 15:34 # @author ninesun # @ para null # @ desc 其中 -b 指定批处理模式, -n 指定迭代次数。 tee读取标准输入的数据,并将其内容输出成文件 -a 代表append,每5 s收集一次 # @ ntoe: awk '{if($8~/^R$/)print}' filter the running state of the gpadmin's PID file_ext=`date +%Y-%m-%d` #head -7;top -c -d 3 -b|grep other|grep -v grep|tee -a|head > /home/scripts/tune/topPid/topc_master_$file_ext.log & top -c -d 5 -b|grep gpadmin|grep -v grep|awk '{if($8~/^R$/)print}'|tee -a|awk '{print strftime("%Y-%m-%d %H:%M:%S"),$0}' > /home/scripts/tune/topPid/topc_master_$file_ext.log #top -c|head -7|top -c -d 5 -b|grep other|grep -v grep|tee -a|awk '{print strftime("%Y-%m-%d %H:%M:%S"),$0}' > /home/scripts/tune/topPid/topc_master_$file_ext.log &
二、切割字符串获取IP
从ip arrd获取某个网卡的ip,并且取最后一个字段。
~]# /sbin/ifconfig bond0|grep "inet addr" inet addr:10.50.10.170 Bcast:10.50.10.255 Mask:255.255.255.0
- -F:指定分隔符
- + : 匹配时表示1个或1个以上
- [:]+ :以1个或多个 : 作为分隔符,对于本例子,这样匹配到的结果如下。
~]# /sbin/ifconfig bond0|grep "inet addr"|awk -F '[:]+' '{print $4}' 255.255.255.0
分开后是这样的
inet addr:
10.50.10.170 Bcast:
10.50.10.255 Mask:
255.255.255.0
这个结果明显不是我们想要的。我们需要的是ip而非mask id。问题出在空格或者冒号。
- [: ]+ :以1个或多个 :(空格或冒号)作为分隔符
严格意义上来说应该用
- || 逻辑或
~]# /sbin/ifconfig bond0|grep "inet addr"|awk -F '[:||" " ]+' '{print $4}' 10.50.10.170
切分后是这样的
inet
addr
10.50.10.170
这时再使用-F [.] 把最后一个字节取到即可达到目的。
scripts]# /sbin/ifconfig bond1|grep "inet addr"|awk -F'[:" "]+' '{print $4}'|awk -F [.] {'print $4'} 169 或者 ~]# /sbin/ifconfig bond0|grep "inet addr"|awk -F '[:||" " ]+' '{print $4}'|awk -F '[.]' '{print $4}' 170
2.1 剥离ip地址 (cut)
1. ~]# ifconfig bond0|sed -n '2p'|cut -d ":" -f2|cut -d " " -f1 2. 10.50.
2.1 剥离ip地址 (awk)
方法一
~]# ifconfig bond0|awk 'NR==2'|awk -F ":" '{print $2}'|awk '{print $1}'
方法二:awk多分隔符方法
]# ifconfig bond0|awk 'NR==2'|awk -F "[: ]+" '{print $4}'
-F 后面的参数意思是:
冒号和空格为分隔符,+ 代表出现一次或多次.
试着把$0 - $4 打印出来看看应该就能明白了
~]# ifconfig bond0|awk 'NR==2'|awk -F "[: ]+" '{print $0}' inet addr:10.50.10. Bcast:10.50 Mask:255.255. ~]# ifconfig bond0|awk 'NR==2'|awk -F "[: ]+" '{print $1}' ~]# ifconfig bond0|awk 'NR==2'|awk -F "[: ]+" '{print $2}' inet ~]# ifconfig bond0|awk 'NR==2'|awk -F "[: ]+" '{print $3}' addr ~]# ifconfig bond0|awk 'NR==2'|awk -F "[: ]+" '{print $4}' 10.50.10.151
2.1 剥离ip地址 (sed)
~]# ifconfig bond0|sed -n '/inet addr/p'|sed -r 's#^.*ddr:(.*)Bc.*$#\1#g'
-n :只打印模式匹配的行
-r :支持扩展表达式
\1 代表真正则match的第一个分组
#只是一个分割符而已,例如这样也可以
tmp]# ifconfig bond0|sed -n '/inet addr/p'|sed -r 's@^.*ddr:(.*)Bc.*$@\1@g' 10.50.10
三、使用awk 修改文本
需求:将时间输入到文本中,用逗号隔开,为后续将数据入库做准备。需要达到这样的目的。
- $0表示整个当前行
- %H 24小时制的小时
- %m 十进制表示的月份
- %M 十时制表示的分钟数
- %S 十进制的秒数
rabbmitMq]# printf 'go_memstats_heap_sys_bytes|23\n' | awk '{print strftime("%Y-%m-%d %H:%M:%S"),$0}' 2020-06-25 19:07:52 go_memstats_heap_sys_bytes|23
3.1 awk内部变量ORS、RS合并拆分行
多行变一行
一行变多行
四、统计进程所占用的内存
每个进程的 PSS ,是指把共享内存平分到各个进程后,再加上进程本身的非共享内存大小的和。就像文档中的这个例子,一个进程的非共享内存为 1000 页,它和另一个进程的共享进程也是 1000 页,那么它的 PSS=1000/2+1000=1500 页。这样,你就可以直接累加 PSS
1、如何统计所有进程的物理内存使用量?
grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kB\n", total }'
2、统计特定进程所占用的内存?
五、awk运算
5.1 find 查找2小时前的文件
awk计算时间
-v 给变得的值 Assign the value val to the variable var
-c 给临时变量赋值
dataTransfer]# find /mnt/dataTransfer/ -maxdepth 1 -type f -ctime -$(awk -v c="120" 'BEGIN{print c/60/24}')
5.2 计算indoe和dentry的大小
GP数据库是一个分布式数据库,master节点主要负责数据汇总和分发。其dentry非常高,在GP4的时期这个数据更夸张,master内存有时候被耗尽。
如何统计其大小做监控呢?
GP4时期是设定OS 的free,少于10%就echo2 >/proc/drop_cache/
如何精确统计其大小呢?
使用awk计算
base]# cat /proc/slabinfo | awk 'BEGIN{OFS=":"}{if($3*$4/1024/1024 > 100) {print $1,$3*$4/1024/1024 "MB"}}' xfs_inode:199.343MB dentry:64980.2MB buffer_head:2697.92MB radix_tree_node:316.038MB kmalloc-1024:105MB kmalloc-64:3651.45MB
5.3 awk 加法、除法应用
有如下文件
[root@master1 /tmp]#more add.txt 2022-06-10 ITSP0200 B102 2244 2022-06-10 ITSP0500 C239 5718 2022-06-10 ITSP0400 B000 1977 2022-06-10 ITSP0200 C105 11 2022-06-10 ITSP0500 B106 287 2022-06-10 ITSP0100 B106 395 2022-06-10 ITSP0400 B106 38 2022-06-10 ITSP0500 B001 31275 2022-06-10 ITSP0500 B102 2168 2022-06-10 ITSP0200 B001 27933 2022-06-10 ITSP0500 B103 1844 2022-06-10 ITSP0100 B102 2389 2022-06-10 ITSP0200 C223 12949 2022-06-10 ITSP0400 B001 8887 2022-06-10 ITSP0400 B102 853 2022-06-10 ITSP0200 B107 1 2022-06-10 ITSP0500 C223 14879 2022-06-10 ITSP0200 C106 8 2022-06-10 ITSP0100 B107 1 2022-06-10 ITSP0100 C223 31486 2022-06-10 ITSP0400 C223 1312 2022-06-10 ITSP0400 C239 1203 2022-06-10 ITSP0100 C239 27757 2022-06-10 ITSP0200 B103 2696 2022-06-10 ITSP0100 C105 14 2022-06-10 ITSP0400 B103 1536 2022-06-10 ITSP0100 B001 30612 2022-06-10 ITSP0200 C239 8300 2022-06-10 ITSP0500 C105 9 2022-06-10 ITSP0200 B106 186 2022-06-10 ITSP0100 B000 2433 2022-06-10 ITSP0500 B000 2791 2022-06-10 ITSP0200 B000 2892 2022-06-11 ITSP0500 B000 756 2022-06-11 ITSP0500 B103 1027
需要统计06/10最后一列的sum,awk如何实现?
awk '/2022-06-10/ { sum += $4}; END { print sum}' add.txt
除法如何保留小数。
[root@master1 /tmp]#a=228867 [root@master1 /tmp]#b=3919 [root@master1 /tmp]#awk 'BEGIN{printf "%.2f\n",'$a'/'$b'}' 58.40 [root@master1 /tmp]# [root@master1 /tmp]#awk 'BEGIN{printf "%.4f\n",'$a'/'$b'}' 58.3993 [root@master1 /tmp]#
六、PV、PV分组、UV、UV分组分析
6.1 PV分析
PV(Page View),用户每访问一个页面就是一次Page View。对于nginx的acess_log来说,分析 PV 非常简单,我们直接使用wc -l就可以看到整体的PV。
6.2 PV分组分析
通常一个日志中可能有几天的 PV,为了得到更加直观的数据,有时候需要按天进行分组。为了简化这个问题,我们先来看看日志中都有哪些天的日志。
使用awk '{print $4}' access.log | less可以看到如下结果。awk是一个处理文本的领域专有语言。这里就牵扯到领域专有语言这个概念,英文是Domain Specific Language。领域专有语言,就是为了处理某个领域专门设计的语言。比如awk是用来分析处理文本的DSL,html是专门用来描述网页的DSL,SQL是专门用来查询数据的DSL……大家还可以根据自己的业务设计某种针对业务的DSL。
你可以看到我们用$4代表文本的第 4 列,也就是时间所在的这一列,如下图所示:
使用awk的substr函数,数字2代表从第 2 个字符开始,数字17代表截取 17 个字符。(到分钟域)
接下来我们就可以分组统计每分钟的日志条数了。
上图中,使用sort进行排序,然后使用uniq -c进行统计。
6.3 分析 UV
UV(Uniq Visitor),也就是统计访问人数。通常确定用户的身份是一个复杂的事情,但是我们可以用 IP 访问来近似统计 UV。
上图中,我们使用 awk 去打印$1也就是第一列,接着sort排序,然后用uniq去重,最后用wc -l查看条数。
按天分组分析每天的 UV 情况。这个情况比较复杂,需要较多的指令,我们先创建一个叫作sum.sh的bash脚本文件,写入如下内容:
#!/usr/bin/bash awk '{print substr($4, 2, 11) " " $1}' access.log |\ sort | uniq |\ awk '{uv[$1]++;next}END{for (ip in uv) print ip, uv[ip]}'
6.4 统计访问最多10个IP
要统计哪个LPI,就把这个KPI作为数组进行统计,类似于sql中的group by.
example]# awk '/14\/Jan\/2022/ {ips[$1]++} END {for(i in ips) {print i,ips[i]}}' access.log | head example]# awk '/14\/Jan\/2022/ {ips[$1]++} END {for(i in ips) {print i,ips[i]}}' access.log | sort -k2 -nr | head
sort -k2 -nr 按照第二列降序按数字排列
6.5 统计访问大于100次的IP
推荐使用方法二。方法一使用了管道,会产生子shell,而子shell的产生是有代价的。
[root@ninesun example]# awk '/14\/Jan\/2022/ {ips[$1]++} END {for(i in ips) {print i,ips[i]}}' access.log | sort -k2 -nr | awk '$2>100' [root@ninesun example]# awk '/14\/Jan\/2022/ {ips[$1]++} END {for(i in ips) { if(ips[i]>100) print i,ips[i]}}' access.log | sort -k2 -nr
6.6 统计访问最多10个页面
example]# awk '/14\/Jan\/2022/ {urls[$7]++} END {for(i in urls) { if(urls[i]>2) print i,urls[i]}}' access.log | sort -k2 -nr |head
6.7 统计每个URL访问的总大小
url作为数组的下标,每次将相同的url的size相加
example]# awk '/14\/Jan\/2022/ {size[$7]+=$10} END { for(i in size) { if(size[i] >1000) print i,size[i] } }' access.log | sort -k2 -nr /client 29844 /sendMessage.do 21730 /login 8000 /mapView/glassList?rows=1500&page=1 2000 /QMSReport/report/ocLightOn/yieldReport.html?_$=1642083572287 1600 /js/com/Regex.js 1400