基础正则三剑客——awk

  • 2019-09-02
  • 0
  • 0

1、基础正则表达式

正则表达式 描述
转义符,将特殊字符进行转义,忽略其特殊意义
\^  匹配行首,awk中,^则是匹配字符串的开始 
匹配行尾,awk中,$则是匹配字符串的结尾
^$  表示空行
匹配除换行符\n之外的任意单个字符
.* 匹配所有
[ ] 匹配包含在[字符]之中的任意一个字符
[^ ] 匹配[^字符]之外的任意一个字符
[ – ]  匹配[]中指定范围内的任意一个字符
? 匹配之前的项1次或者0次
+ 匹配之前的项1次或者多次
* 匹配之前的项0次或者多次, .*
()  匹配表达式,创建一个用于匹配的子串 
{ n }  匹配之前的项n次,n是可以为0的正整数
{n,}     之前的项至少需要匹配n次
{n,m} 指定之前的项至少匹配n次,最多匹配m次,n<=m
交替匹配 两边的任意一项ab(c d)匹配abc或abd

2、特定字符

 
>[[:space:]]   空格
[[:digit:]]  [0-9]
[[:lower:]]  [a-z]
[[:upper:]]  [A-Z]
[[:alpha:]]   [a-Z]

3、Awk文本处理

awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。
awk数据可以来自标准输入、一个或多个文件,或其它命令的输出。
awk通常是配合脚本进行使用, 是一个强大的文本处理工具。

3.1、awk 的处理数据的方式

1.进行逐行扫描文件, 从第一行到最后一行
2.寻找匹配的特定模式的行,在行上进行操作
3.如果没有指定处理动作,则把匹配的行显示到标准输出
4.如果没有指定模式,则所有被操作的行都被处理

3.1、awk的语法格式及选项

# 格式
awk [options] 'commands' filenames 
awk [options] -f awk-script-file filenames

# 选项
-F 定义输入字段分隔符,默认的分隔符, 空格或tab键

命令 command
行处理前        行处理     行处理后
BEGIN{}        {}       END{}
BEGIN发生在读文件之前
[root@shell ~]# awk 'BEGIN{print 1/2}' 
0.5

BEGIN在行处理前, 修改字段分隔符
[root@Shell ~]# awk 'BEGIN{FS=":"} {print $1}' /etc/passwd

BEGIN在行处理前, 修改字段读入和输出分隔符
[root@Shell ~]# awk 'BEGIN{FS=":";OFS="---"} {print $1,$2}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{print 1/2} {print "ok"} END {print "Game Over"}'  /etc/hosts
0.5
ok
Game Over

3.2、awk命令格式

3.2.1、匹配 awk ‘pattern’ filename

[root@Shell ~]# awk '/root/' /etc/passwd

3.2.2、处理动作 awk ‘{action}’ filename

[root@Shell ~]# awk -F: '{print $1}' /etc/passwd

3.2.3、匹配+处理动作 awk ‘pattern {action}’ filename

[root@Shell ~]# awk -F ':' '/root/ {print $1,$3}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd

3.2.4、判断大于多少则输出什么内容 command |awk ‘pattern {action}’

[root@Shell ~]# df |awk '/\/$/ {if ($3>50000) print $4}'

3.3、AWK工作原理

# awk -F: '{print $1,$3}' /etc/passwd`
1.awk将文件中的每一行作为输入, 并将每一行赋给内部变量`$0`, 以换行符结束
2.awk开始进行字段分解,每个字段存储在已编号的变量中,从`$1`开始[默认空格分割]
3.awk默认字段分隔符是由内部`FS`变量来确定, 可以使用`-F`修订
4.awk行处理时使用了`print`函数打印分割后的字段
5.awk在打印后的字段加上空格,因为`$1,$3` 之间有一个逗号。逗号被映射至`OFS`内部变量中,称为输出字段分隔符, `OFS`默认为空格.
6.awk输出之后,将从文件中获取另一行,并将其存储在`$0`中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕.

3.4、Awk内部变量

# 1.`$0`保存当前记录的内容*
[root@Shell ~]# awk '{print $0}' /etc/passwd

# 2.`NR`记录输入总的编号(行号)*
[root@Shell ~]# awk '{print NR,$0}' /etc/passwd
[root@Shell ~]# awk 'NR<=3' /etc/passwd

# 3.`FNR`当前输入文件的编号(行号)*
[root@Shell ~]# awk '{print NR,$0}' /etc/passwd /etc/hosts
[root@Shell ~]# awk '{print FNR,$0}' /etc/passwd /etc/hosts

# 4.`NF`保存行的最后一列*
[root@Shell ~]# awk -F ":" '{print NF,$NF}' /etc/passwd /etc/hosts

# 5.`FS`指定字段分割符, 默认是空格*
以冒号作为字段分隔符
[root@Shell ~]# awk -F: '/root/{print $1,$3}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd
以空格冒号tab作为字段分割
[root@Shell ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd

# 6.`OFS`指定输出字段分隔符*
逗号映射为OFS, 初始情况下OFS变量是空格
[root@Shell ~]# awk -F: '/root/{print $1,$2,$3,$4}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2}' /etc/passwd

# 7.`RS`输入记录分隔符,默认为换行符[了解]*
[root@Shell ~]# awk -F: 'BEGIN{RS=" "} {print $0}' /etc/hosts

# 8.`ORS`将文件以空格为分割每一行合并为一行[了解]*
[root@Shell ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' /etc/hosts

# 9.`print`格式化输出函数*
[root@Shell ~]# date|awk '{print $2,"5月份""\n",$NF,"今年"}'
[root@Shell ~]# awk -F: '{print "用户是:" $1 "\t 用户uid: " $3  "\t 用户gid:" $4}' /etc/passwd

# printf 函数
[root@Shell ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1, $2, $3}' /etc/passwd
%s 字符类型
%d 数值类型
占 15 字符
- 表示左对齐,默认是右对齐
printf 默认不会在行尾自动换行,加\n

3.5 Awk模式动作

awk语句都由模式和动作组成。
模式部分决定动作语句何时触发及触发事件。
如果省略模式部分,动作将时刻保持执行状态。模式可以是条件语句或复合语句或正则表达式。

3.5.1、正则表达式*

匹配记录(整行)
[root@Shell ~]# awk '/^root/' /etc/passwd
[root@Shell ~]# awk '$0 ~ /^root/' /etc/passwd
匹配字段:匹配操作符(~ !~)
[root@Shell ~]# awk '$1~/^root/' /etc/passwd
[root@Shell ~]# awk '$NF !~ /bash$/' /etc/passwd

3.5.2、比较表达式*

比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。
比较表达式使用关系运算符,用于比较数字与字符串。

关系运算符

运算符 含义 示例
< 小于 x<y
<= 小于或等于 x<=y 
== 等于   x==y
!= 不等于 x!=y
>= 大于等于 x>=y
> 大于 x>y
# uid为0的列出来
[root@Shell ~]# awk -F ":" '$3==0' /etc/passwd

# uid小于10的全部列出来
[root@Shell ~]# awk -F: '$3 < 10' /etc/passwd

# 用户登陆的shell等于/bin/bash
[root@Shell ~]# awk -F: '$7 == "/bin/bash" ' /etc/passwd

# 第一列为alice的列出来
[root@Shell ~]# awk -F: '$1 == "alice" ' /etc/passwd 

# 为alice的用户列出来
[root@Shell ~]# awk -F: '$1 ~ /alice/ ' /etc/passwd 
[root@Shell ~]# awk -F: '$1 !~ /alice/ ' /etc/passwd

# 磁盘使用率大于多少则,则打印可用的值
[root@Shell ~]# df |awk '/\/$/'|awk '$3>1000000 {print $4}'

3.5.3、条件表达式

[root@Shell ~]# awk -F: '$3>300 {print $0}' /etc/passwd
[root@Shell ~]# awk -F: '{if($3>300) print $0}' /etc/passwd
[root@Shell ~]# awk -F: '{if($3>5555){print $3} else {print $1}}' /etc/passwd

3.5.4、运算表达式*

[root@Shell ~]# awk -F: '$3 * 10 > 500000' /etc/passwd
[root@Shell ~]# awk -F: 'BEGIN{OFS="--"} { if($3*10>50000) {print $1,$3} } END {print "打印ok"}' /etc/passwd
[root@Shell ~]# awk '/southem/{print $5 + 10}' datafile 
[root@Shell ~]# awk '/southem/{print $5 + 10.56}' datafile
[root@Shell ~]# awk '/southem/{print $8 - 10}' datafile 
[root@Shell ~]# awk '/southem/{print $8 / 2 }' datafile  
[root@Shell ~]# awk '/southem/{print $8 * 2 }' datafile 
[root@Shell ~]# awk '/southem/{print $8 % 2 }' datafile

3.5.5、逻辑操作符和复合模式*

# &&逻辑与 || 逻辑或  !逻辑非 

# 匹配用户名为root并且打印uid小于15的行
[root@Shell ~]# awk -F: '$1~/root/ && $3<=15' /etc/passwd 

# 匹配用户名为root或uid大于5000
[root@Shell ~]# awk -F: '$1~/root/ || $3>=5000' /etc/passwd

3.6、awk示例

# awk '/west/' datafile 
# awk '/^north/' datafile 
# awk '$3 ~ /^north/' datafile 
# awk '/^(no|so)/' datafile 
# awk '{print $3,$2}' datafile
# awk '{print $3 $2}' datafile 
# awk '{print $0}' datafile 
# awk '{print "Number of fields: "NF}' datafile 
# awk '/northeast/{print $3,$2}' datafile
# awk '/^[ns]/{print $1}' datafile 
# awk '$5 ~ /\. [7-9]+/' datafile 
# awk '$2 !~ /E/{print $1,$2}' datafile 
# awk '$3 ~ /^Joel/{print $3 "is a nice boy."}' datafile 
# awk '$8 ~ /[0-9][0-9]$/{print $8}' datafile
# awk '$4 ~ /Chin$/{print "The price is $" $8 "."}' datafile 
# awk '/Tj/{print $0}' datafile 
# awk -F: '{print "Number of fields: "NF}' /etc/passwd 
# awk -F"[ :]" '{print NF}' /etc/passwd 
[root@Shell ~]# cat b.txt 
lzy ljc:is a:good boy!

[root@Shell ~]# awk '{print NF}' b.txt
4

[root@Shell ~]# awk -F ':' '{print NF}' b.txt
3

[root@Shell ~]# awk -F"[ :]" '{print NF}' b.txt
6

3.7、awk条件判断

# if语句格式:{ if(表达式){语句;语句;... }}

打印当前管理员用户名称
[root@Shell ~]# awk -F: '{ if($3==0){print $1 "is adminisitrator"} }' /etc/passwd

统计系统用户数量
[root@Shell ~]# awk -F: '{ if($3>0 && $3<1000){i++}} END {print i}' /etc/passwd

统计普通用户数量
[root@Shell ~]# awk -F: '{ if($3>1000){i++}} END {print i}' /etc/passwd
# if...else 语句格式: {if(表达式){语句;语句;... }else{语句;语句;...}}

awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd
awk -F: '{if($3==0) {count++} else{i++} }' /etc/passwd
awk -F: '{if($3==0){count++} else{i++}} END{print " 管理员个数: "count ; print " 系统用户数: "i}' /etc/passwd
# if...else if...else 语句格式: 
# {if(表达式 1){语句;语句;... }else if(表达式 2){语句;语句;. .. }else{语句;语句;... }}

[root@Shell ~]# awk -F: '{ if($3==0){i++} else if($3>0 && $3<1000){j++} else if($3>1000) {k++}} END {print i;print j;print k}' /etc/passwd
[root@Shell ~]# awk -F: '{ if($3==0){i++} else if($3>0 && $3<1000){j++} else if($3>1000) {k++}} END {print "管理员个数"i; print "系统用户个数" j; print "系统用户个 数" }' /etc/passwd
管理员个数1
系统用户个数29
系统用户个数69

3.8、Awk循环语句

3.8.1、while循环

[root@Shell ~]# awk 'BEGIN{ i=1; while(i<=10){print i; i++} }'
[root@Shell ~]# awk -F: '{i=1; while(i<=NF){print $i; i++}}' /etc/passwd
[root@Shell ~]# awk -F: '{i=1; while(i<=10) {print $0; i++}}' /etc/passwd
[root@Shell ~]#cat b.txt
111 222
333 444 555
666 777 888 999
[root@Shell ~]# awk '{i=1; while(i<=NF){print $i; i++}}' b.txt

3.8.2、for循环

C 风格 for
[root@Shell ~]# awk 'BEGIN{for(i=1;i<=5;i++){print i} }' 
将每行打印 10 次
[root@Shell ~]# awk -F: '{ for(i=1;i<=10;i++) {print $0} }' passwd
[root@Shell ~]# awk -F: '{ for(i=1;i<=10;i++) {print $0} }' passwd 
[root@Shell ~]# awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd

3.9、awk数组

将需要统计的某个字段作为数组的索引,然后对索引进行遍历

3.9.1、统计/etc/passwd中各种类型shell 的数量*

# awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }' /etc/passwd

3.9.2、站访问状态统计<当前时实状态ss>*

[root@Shell ~]# ss -an|awk '/:80/{tcp[$2]++} END {for(i in tcp){print i,tcp[i]}}'

3.9.3、统计当前访问的每个IP的数量<当前时实状态 netstat,ss>*

[root@Shell ~]# ss -an|awk -F ':' '/:80/{ips[$(NF-1)]++} END {for(i in ips){print i,ips[i]}}'

3.9.4、awk数组案例

`Nginx`日志分析,日志格式如下:
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

52.55.21.59 - - [25/Jan/2018:14:55:36 +0800] "GET /feed/ HTTP/1.1" 404 162 "https:www.google.com/" "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52" "-"

# 统计2018年01月25日,当天的PV量*
[root@Shell ~]# grep "25/Jan/2018" log.bjstack.log |wc -l
[root@Shell ~]# awk "/25\/Jan\/2018/" log.bjstack.log |wc -l
[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips) {sum+=ips[i]} {print sum}}' log.bjstack.log

# 统计15-19点的pv量
[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00 {print $0}"' log.bjstack.log |wc -l

# 统计2018年01月25日,一天内访问最多的10个IP*
[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips){ print ips[i],i}}' log.bjstack.log |sort -rn|head

# 统计15-19点访问次数最多的10个IP
[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00"' log.bjstack.log |awk '{ips[$1]++} END {for(i in ips){print ips[i],i}}'|sort -rn|head

# 统计2018年01月25日,访问大于100次的IP*
[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips){if(ips[i]>10){print i,ips[i]}}}' log.bjstack.log

# 统计2018年01月25日,访问最多的10个页面($request top 10)*
[root@Shell ~]# awk '/25\/Jan\/2018/ {request[$7]++} END {for(i in request){print request[i],i}}' log.bjstack.log |sort -rn|head

# 统计2018年01月25日,每个URL访问内容总大小($body_bytes_sent)*
[root@Shell ~]# awk '/25\/Jan\/2018/ {request[$7]++;size[$7]+=$10} END {for(i in request){print request[i],i,size[i]}}' log.bjstack.log |sort -rn|head

# 统计2018年01月25日,每个IP访问状态码数量($status)*
[root@Shell ~]# awk '{ip_code[$1 " " $9]++} END {for(i in ip_code){print ip_code[i],i}}' log.bjstack.log|sort -rn|head

# 统计2018年01月25日,访问状态码为404及出现的次数($status)*
[root@Shell ~]# grep "404" log.bjstack.log |wc -l
[root@Shell ~]# awk '{if($9=="404") code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

# 统计2018年01月25日,8:30-9:00访问状态码是404*
[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00" && $9=="404" {code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log
[root@Shell ~]# awk '$9=="404" {code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

# 统计2018年01月25日,各种状态码数量,统计状态码出现的次数
[root@Shell ~]# awk '{code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log
[root@Shell ~]# awk '{if($9>=100 && $9<200) {i++}
else if ($9>=200 && $9<300) {j++}
else if ($9>=300 && $9<400) {k++}
else if ($9>=400 && $9<500) {n++}
else if($9>=500) {p++}}
END{print i,j,k,n,p,i+j+k+n+p}' log.bjstack.log

评论

还没有任何评论,你来说两句吧

提供支持 - 友情链接 - 衫小寨