一、linux文本分析利器awk命令使用详解及详细示例
grep,sed,awk是linux里文本处理的三大利器,awk是行处理器(依次对每一行进行处理,然后输出),其优点是在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息 publish:August 11, 2017 -Friday。
#awk命令形式:
awk [-F|-f|-v] 'BEGIN{} /preg/{command1; command2} END{}' file
1. awk命令选项,及命令格式详解
#选项,及命令格式详解
[-F|-f|-v] #大参数一般不需要用,默认是为空格分界,-F指定分隔符,-f调用脚本,-v定义变量 var=value
' ' #引用代码块
BEGIN #初始化代码块,在对每一行进行处理之前,初始化代码
// #内容匹配,只对匹配通过的行进行处理
{} #命令代码块,可执行多条命令,多条命令使用分号分隔
END #结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息
命令中可用的一些变量等数据如下:
$0 #表示整个当前行,$1表示第1项,以此类推(以分隔符分开的各项,不包括分隔符)
NF #字段数量总量,即以分隔符号分开后当前行的字段总数
NR #每行的记录号,多文件记录递增,相当于行数
FNR #与NR类似,不过多文件记录不递增,每个文件都从1开始
\t #制表符
\n #换行符
FS #BEGIN时定义分隔符
RS #输入的记录分隔符, 默认为换行符(即文本是按一行一行输入)
~ #匹配,与==相比不是精确比较
!~ #不匹配,不精确比较
== #等于,必须全部相等,精确比较
!= #不等于,精确比较
&& #逻辑与
|| #逻辑或
+ #匹配时表示1个或1个以上
* #通配,0个或多个
FILENAME #分析的文件名
OFS #输出字段分隔符, 默认也是空格,可以改为制表符等
ORS #输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕
-F'[:#/]' #定义三个分隔符
2. AWK命令使用详细示例
以下为一个日志文件,记录了请求时间,响应状态,请求的IP,以及响应的时间。总共约50万条数据。
[root@123 ~]# wc -l s.log
511386 s.log
[root@123 ~]# head -5 s.log
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[11/Aug/2017:00:00:02] 200 221.229.202.76" 220.229
[11/Aug/2017:00:00:02] 200 221.229.202.75" 220.229
[11/Aug/2017:00:00:02] 200 221.229.202.76" 220.229
[root@123 ~]# awk -F ':' '{print $1,$3}' s.log | head -5 #使用冒号分隔,取出第1项和第3项,
[11/Aug/2017 00
[11/Aug/2017 00
[11/Aug/2017 00
[11/Aug/2017 00
[11/Aug/2017 00
[root@123 ~]# awk -F ':' '{print $1,$3}' OFS='=' s.log | head -5 #使用冒号分隔,取出第1项和第3项,但指定了输出字段分隔符=,如下:
[11/Aug/2017=00
[11/Aug/2017=00
[11/Aug/2017=00
[11/Aug/2017=00
[11/Aug/2017=00
[root@123 ~]# head -5 s.log | awk -F ':' '{print $1,$3}' OFS='=' ORS=' -kkk- ' #指定了记录分隔符,如下就全部输出在一行上。
[11/Aug/2017=00 -kkk- [11/Aug/2017=00 -kkk- [11/Aug/2017=00 -kkk- [11/Aug/2017=00 -kkk- [11/Aug/2017=00 -kkk- [root@123 ~]#
[root@123 ~]# awk -F '[: ]' '{print $1,$5}' s.log | head -5 #可以指定多个分隔符,比如这里同时以冒号和空格分隔开。
[11/Aug/2017 200
[11/Aug/2017 200
[11/Aug/2017 200
[11/Aug/2017 200
[11/Aug/2017 200
[root@123 ~]# awk '{print $0}' s.log | head -2 #$0代表本行记录,如下
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[root@123 ~]# awk '{print $NF}' s.log | head -2 #将每行第NF个字段的值打印出来,也就是最后一个字段。
35.25
35.25
[root@123 ~]# awk '{print NF}' s.log | head -2 #打印每行有多少个字段数
4
4
[root@123 ~]# awk '{print $1,$4}' s.log | head -2 #逗号分开打印时即以空格分开
[11/Aug/2017:00:00:02] 35.25
[11/Aug/2017:00:00:02] 35.25
[root@123 ~]# awk '{print $1$4}' s.log | head -2 #连在一起输出,和awk '{print $1 $4}' s.log | head -2 结果是一样的。
[11/Aug/2017:00:00:02]35.25
[11/Aug/2017:00:00:02]35.25
[root@123 ~]# awk '{print $1"-=-="$4}' s.log | head -2 #自定义连接字符,这个比OFS更灵活
[11/Aug/2017:00:00:02]-=-=35.25
[11/Aug/2017:00:00:02]-=-=35.25
[root@123 ~]# awk 'NF==4 {print NR,$4}' s.log | head -2 只显示只有4个字段的行。也可以使用大于小于,并且输出行号
1 35.25
2 35.25
[root@123 ~]# awk 'NR==2 || NR==3 {print NR,$4}' s.log | head -2 #只打印第2行和第3行
2 35.25
3 220.229
[root@123 ~]# awk '/221.229/' s.log | head -2 #字符匹配,只输出包含221.229的行,和命令:awk '/221.229/{print $0}' s.log | head -2一样。
#也和这条命令:awk '/221.229/{print}' s.log | head -2 执行结果一样。
[11/Aug/2017:00:00:02] 200 221.229.202.76" 220.229
[11/Aug/2017:00:00:02] 200 221.229.202.75" 220.229
[root@123 ~]# awk '!/221.229/{print}' s.log | head -2 #取反,不匹配才输出
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[root@123 ~]# awk '/:00:10:01/,/:00:10:02/{print $0}' s.log #区间匹配,只匹配10:01秒到02秒的内容,但是注意,02秒的只匹配第一行就停止。
[11/Aug/2017:00:10:01] 200 101.28.249.34" 100.28
[11/Aug/2017:00:10:01] 200 221.229.202.75" 220.229
[11/Aug/2017:00:10:01] 200 36.250.93.93" 35.25
[11/Aug/2017:00:10:01] 200 221.229.202.75" 220.229
[11/Aug/2017:00:10:01] 200 221.229.202.75" 220.229
[11/Aug/2017:00:10:01] 200 221.229.202.76" 220.229
[11/Aug/2017:00:10:01] 200 101.28.249.41" 100.28
[11/Aug/2017:00:10:01] 200 221.229.202.88" 220.229
[11/Aug/2017:00:10:02] 200 36.250.93.93" 35.25
[root@123 ~]# awk '/.207$/{print $0}' s.log | head -2 #指定结尾字符串必须满足的条件,也可以使用^指定开头字符串的条件。
[11/Aug/2017:00:00:11] 200 113.207.6.193" 112.207
[11/Aug/2017:00:00:14] 200 113.207.6.193" 112.207
[root@123 ~]# awk '/[3][4][5]*/{print $0}' s.log | head -2 #匹配包含3和4,5可有可无
[11/Aug/2017:00:00:03] 200 101.28.249.34" 100.28
[11/Aug/2017:00:00:07] 200 101.28.249.34" 100.28
[root@123 ~]# awk '/[3][4][5]+/{print $0}' s.log | head -2 #必须包含5,没有找到记录。
[root@123 ~]# awk '/[2][0][7]+/{print $0}' s.log | head -2
[11/Aug/2017:00:00:11] 200 113.207.6.193" 112.207
[11/Aug/2017:00:00:14] 200 113.207.6.193" 112.207
[root@123 ~]# awk '{ if( $2 == 404 ) print $0}' s.log | head -2 #对某个字段判断是否=再输出。
[11/Aug/2017:17:58:47] 404 61.200.88.14" 60.2
[11/Aug/2017:17:58:51] 404 61.200.88.12" 60.2
[root@123 ~]# awk '{ if( $2 == 404 && $1 ~/47]/ ) print $0}' s.log | head -2 #要求404,并且是第47秒出现的行。
[11/Aug/2017:17:58:47] 404 61.200.88.14" 60.2
[root@123 ~]# awk '{ if( $2 != 200 ) print $0}' s.log #要求输出所有不是200的响应结果。
[11/Aug/2017:11:25:23] 304 101.28.249.32" 100.28
[11/Aug/2017:17:58:47] 404 61.200.88.14" 60.2
[11/Aug/2017:17:58:51] 404 61.200.88.12" 60.2
[root@123 ~]# awk '{if ($2 ==304 || $2 ~/404/ ) print $0}' s.log #IF语句,必须用在{}中,且比较内容用()扩起来
[11/Aug/2017:11:25:23] 304 101.28.249.32" 100.28
[11/Aug/2017:17:58:47] 404 61.200.88.14" 60.2
[11/Aug/2017:17:58:51] 404 61.200.88.12" 60.2
[root@123 ~]# awk '{if($4>0 && $4<50) print $0;else print $4}' s.log | head -5 #if else结构
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
[11/Aug/2017:00:00:02] 200 36.250.93.93" 35.25
220.229
220.229
220.229
3. AWK使用格式化输出
关于awk中使用printf进行格式化输出。及BEGIN,END命令格式。
netstat -anp|awk '{printf "%-8s %-8s %-10s\n",$1,$2,$3}'
printf表示格式输出
%格式化输出分隔符,-8长度为8个字符,s表示字符串类型
打印每行前三个字段,指定第一个字段输出字符串类型(长度为8),第二个字段输出字符串类型(长度为8),第三个字段输出字符串类型(长度为10)
netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-10s %-10s %-10s \n",$1,$2,$3}'
netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-3s %-10s %-10s %-10s \n",NR,$1,$2,$3}'
命令使用示例:
[root@123 ~]# awk '{printf "%-16s %-7s\n",$3,$4}' s.log | head -4 #要求IP占用16位,响应时间占用6位,这样就对齐了。
36.250.93.93" 35.25
36.250.93.93" 35.25
221.229.202.76" 220.229
221.229.202.75" 220.229
[root@123 ~]# awk 'BEGIN{i=1};/.25$/{ if($2==200 && $4>1 && $4<40) print $0};' s.log | wc -l #命令BEGIN
129117
[root@123 ~]# awk 'BEGIN{i=1};/.25$/{ if($2==200 && $4>1 && $4<40) {i++}};END{print i}' s.log
129118
[root@123 ~]#
4. awk中使用数组并进行相关统计:
A,统计各种响应状态码的各数。
[root@123 ~]# awk 'BEGIN{no=1};{a[$2]++};END{for (i in a) print i,a[i]}' s.log
200 511383
304 1
404 2
[root@123 ~]# awk '{a[$2]++};END{for (i in a) print i,a[i]}' s.log
200 511383
304 1
404 2
[root@123 ~]# awk '{print $2}' s.log | sort | uniq -c | sort -n -k1 #也可以使用
1 304
2 404
511383 200
B,计算平均响应时间
比如要求响应状态为200,且响应时间不能为-(即不要404,304等结果)
[root@123 ~]# awk 'BEGIN{t=0;n=0};{if($2==200 && $4>0) {t+=$4;n++;} } END{ print t,n,t/n}' s.log
7.18456e+07 510623 140.702
C,统计响应时间最长的和最短的时间。
[root@123 ~]# awk 'BEGIN{min=10000000;max=0};{if($2 && $4>0 && $4<min) {min=$4}; if($2 && $4>0 && $4>max) {max=$4}; } END{ print min,max}' s.log
13.18 222.151
二、linux服务器上手动释放内存和交换内存的详细介绍及方法
linux中的内存占用很多时候一看都占用很大,因为linux的原则是尽可能地充分利用内存,所以大部分内存用来cache了。之前也有一篇文章:安装centos开机出现Kernel panic - not syncing: Attempted to kill init无法启动解决方法 及 linux内存一直减少-Cached占用很大的解决_attempted to kill init进不了系统-CSDN博客。Linux上清缓存是有三种选项来清除缓存(包括Buffer和Cache),清除时不用中断任何进程及服务。
仅清除页面缓存(PageCache)
# echo 1 > /proc/sys/vm/drop_caches
清除目录项和inode
# echo 2 > /proc/sys/vm/drop_caches
清除页面缓存,目录项和inode
# echo 3 > /proc/sys/vm/drop_caches
#演示示例:执行命令前先查看下内存
[root@123 04007cn]# free -m
total used free shared buffers cached
Mem: 992 597 394 0 127 207
-/+ buffers/cache: 263 728
Swap: 3007 0 3007
[root@123 04007cn]# echo 1 > /proc/sys/vm/drop_caches
# 执行完后内存对比
[root@123 04007cn]# free -m
total used free shared buffers cached
Mem: 992 275 716 0 0 17
-/+ buffers/cache: 257 734
Swap: 3007 0 3007
上述命令说明:清除前一定要记得执行一下sync命令,sync命令是刷新文件系统缓冲区(buffer),有些执行命令比如文件修改可能还留在缓冲区未对硬盘上的物理文件进行修改,如果直接执行清除可能导致这些命令丢失,引起问题。在生产环境中建议清除1即可。
清除Linux的交换内存,可以运行下面的命令:
# swapoff -a && swapon -a