awk
Linux三剑客
grep
-n:打印行号
-A:after 打印过滤内容的后N行
-B:before 打印过滤内容的前N行
-C:center 打印过滤内容的前后N行
-E:支持扩展正则 ere grep -E 'root|nginx' /etc/passwd
-v:取反
-o:只打印匹配到的内容
-w:精确匹配
-P:支持Perl正则
-i:忽略大小写
-r:递归检索文件中的内容 grep -r 'www.baidu.com' ./
-R:递归检索文件中的内容,包括软链接文件 grep -R 'www.baidu.com' ./
-l:只显示文件名
-h:只显示文件内容
-f:对比文件内容,内容少的文件在前面,内容多的文件在后面,取反可以看到不同的文件内容
-c:统计行数,类似于 wc -l
-G:支持基础正则 bre
-m:显示前N行 类似于 head -n
[root@m01 web]# grep -r 'www.zls.com' /root/web/
/root/web/css/style.css:www.zls.com
/root/web/js/main.js:www.zls.com
/root/web/index.html:www.zls.com
[root@m01 web]# grep -lr 'www.zls.com' /root/web/|xargs sed -i.zls 's#www.zls.com#www.baidu.com#g'
awk
sed
在sed中,我们核心的内容,主要分为四个部分:
- 增
- 删
- 改
- 查
当然我们还有一些进阶内容:模式空间与保持空间
sed命令执行流程
举个例子:
# 以下是zls.txt文件内容
1,zls,666
2,wls,777
3,cls,888
4,lls,999
执行 sed -n '3p' zls.txt
命令后,sed都做了啥?
1.sed先是按行读取文件内容
2.每读取一行内容,都会进行一次判断,是不是你想要的行
3.如果不是,则判断是不是加了-n选项
4.如果加了-n,就读取下一行
5.如果没加-n,就会将所有内容输出到命令行(默认输出)
6.如果是,你想要的那一行(第三行)则判断执行的后续动作(p d s a i c)
7.动作处理完成后,输出指定的内容
8.即便是读取完了,内容也输出了,sed也会继续往后读,直到文件的最后一行
sed - 查
sed命令选项 | 选项含义 | sed命令动作 | 动作含义 |
---|---|---|---|
-n | 取消默认输出 | p | print打印 |
-r | 支持扩展正则 | d | delete删除 |
a | append追加 | ||
i | insert插入 |
p:打印,显示
## sed显示单行内容
[root@m01 ~]# sed '3p' zls.txt
1,zls,666
2,wls,777
3,cls,888
3,cls,888
4,lls,999
## sed取消默认输出
[root@m01 ~]# sed -n '3p' zls.txt
3,cls,888
## sed显示多行内容并取消默认输出
[root@m01 ~]# sed -n '1,3p' zls.txt
1,zls,666
2,wls,777
3,cls,888
## sed模糊查询
[root@m01 ~]# sed -n '/zls/p' zls.txt
1,zls,666
[root@m01 ~]# sed -nr '/zls|cls/p' zls.txt
1,zls,666
3,cls,888
[root@m01 ~]# sed -n '/zls/,/cls/p' zls.txt
1,zls,666
2,wls,777
3,cls,888
## sed实现 grep -A
[root@m01 ~]# sed -n '/zls/,+2p' zls.txt
1,zls,666
2,wls,777
3,cls,888
[root@m01 ~]# grep 'zls' -A 2 zls.txt
1,zls,666
2,wls,777
3,cls,888
## sed 隔指定行数读取一行
[root@m01 ~]# sed -n '1~2p' zls.txt
1,zls,666
3,cls,888
[root@m01 ~]# sed -n '1~3p' zls.txt
1,zls,666
4,lls,999
sed - 删
d:delete 删除
## 删除指定行数,不修改原文件
[root@m01 ~]# sed '2d' zls.txt
## 删除最后一行
[root@m01 ~]# sed '$d' zls.txt
1,zls,666
3,cls,888
## 包含zls的行到cls的行都删掉
[root@m01 ~]# sed -n '/zls/,/cls/d' zls.txt
sed - 增
菜
c:replace 替换整行内容
[root@m01 ~]# cat zls.txt
1,zls,666
3,cls,888
4,lls,999
[root@m01 ~]# sed '2c2,huanglong,438' zls.txt
1,zls,666
2,huanglong,438
4,lls,999
a:append 追加
[root@m01 ~]# sed '$a5,huanglong,438' zls.txt
1,zls,666
3,cls,888
4,lls,999
5,huanglong,438
[root@m01 ~]# sed '2a5,huanglong,438' zls.txt
1,zls,666
3,cls,888
5,huanglong,438
4,lls,999
[root@m01 ~]# sed '1a2,huanglong,438' zls.txt
1,zls,666
2,huanglong,438
3,cls,888
4,lls,999
i:insert 插入
[root@m01 ~]# sed '$i2,huanglong,438' zls.txt
1,zls,666
3,cls,888
2,huanglong,438
4,lls,999
[root@m01 ~]# sed '1i2,huanglong,438' zls.txt
2,huanglong,438
1,zls,666
3,cls,888
4,lls,999
sed - 改
s:substitute 替换
g:global 全局
s###g
s@@@g
s啥都行g
## 基础用法
[root@zabbix01 ~]# sed 's#zls#ZLS#g' zls.txt
1,ZLS,666
2,wls,777
3,cls,888
4,lls,999
## 使用正则
[root@zabbix01 ~]# sed 's#[0-9]#666#g' zls.txt
666,zls,666666666
666,wls,666666666
666,cls,666666666
666,lls,666666666
## 后向引用
[root@m01 ~]# ifconfig eth0|sed -nr 's#^.*inet (.*) net.*#\1#gp'
10.0.0.61
[root@m01 ~]# ip a s eth1|sed -nr 's#^.*inet (.*)/24.*#\1#gp'
172.16.1.61
sed的模式空间
将文件中的,所有换行符,替换成空格
N:在读取文件是,让sed把下一行内容一起读进去
awk
awk的内置变量和动作和选项
awk内置变量 | 变量含义 | awk选项 | 选项含义 | awk动作 | 动作含义 |
---|---|---|---|---|---|
NR | Number of Record 行号 | -F | 指定分隔符 | gsub | 替换 |
RS | Record Separator 行的分隔符(\n) | -v | 指定变量(内置变量、自定义变量) | 打印 | |
FS | Field Separator 列的分隔符(空格) | ||||
NF | Number Of Filed 每一行有多少列 |
注意:awk输出变量使用单引号,bash输出变量使用双引号
awk
不是一个命令,是一门语言。
awk
又叫做GNU awk,gawk
[root@m01 ~]# ls -l $(which awk)
lrwxrwxrwx. 1 root root 4 Jul 5 2021 /usr/bin/awk -> gawk
awk执行流程
三个阶段
- 读取文件之前
- BEGIN{}
- 1.读取文件之前,先看命令的选项,例如 -F,-v
- 2.如果写了BEGIN{}则先在BEGIN{}中的指令
- 读取文件时
- {}
- 1.awk在读取文件时,也是一行一行的读\
- 2.读取一行之后,判断是否满足条件,如果是,则执行
- 3.如果不满足条件,awk继续读取下一行,直到满足条件或者到文件的最后一行
- 读取文件之后
- END{}
- 1.所有文件读取完成之后,走END{}中的指令
[root@m01 ~]# awk 'BEGIN{xxx}{print $1}END{print 1/3}' zls.txt
[root@m01 ~]# awk -F: 'BEGIN{print "name","uid"}{print $1,$3}END{print "文件处理完成"}' /etc/passwd|column -t
[root@m01 ~]# awk -F: 'BEGIN{print "name","uid","gid"}{print $1,$3,$4}END{print "sb"}' /etc/passwd|column -t
name uid gid
root 0 0
bin 1 1
daemon 2 2
adm 3 4
lp 4 7
sync 5 0
shutdown 6 0
halt 7 0
mail 8 12
operator 11 0
games 12 100
ftp 14 50
awk的行与列
行:记录 record
列:字段 field
awk取行
NR:Number of Record
[root@m01 ~]# awk 'NR==1' /etc/passwd
root:x:0:0:root:/root:/bin/bash
## 范围取行
[root@m01 ~]# awk 'NR>=1 && NR<=3' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@m01 ~]# awk 'NR==1,NR==3' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
## 包含zls和cls的行
[root@m01 ~]# awk '/zls|cls/' zls.txt
1,zls,666
3,cls,888
zls,111
[root@m01 ~]# awk 'NR>=3' zls.txt
/tmp/check_2.txt
/tmp/check_3.txt
/tmp/check_4.txt
/tmp/check_5.txt
3,cls,888
zls,111
4,lls,999
[root@m01 ~]# awk '/zls/,/cls/' zls.txt
1,zls,666
/tmp/check_1.txt
/tmp/check_2.txt
/tmp/check_3.txt
cls
zls
4,lls,999
cls
### awk的控制结束标记
Record Separator
[root@m01 ~]# awk -vRS=, 'NR==1' zls.txt
awk取列
FS:内置变量,列分隔符 -F: = -vFS=:
[root@m01 ~]# awk -vFS=: '{print $1}' /etc/passwd
[root@m01 ~]# awk -vFS=: '{print $1,$NF}' /etc/passwd
[root@m01 ~]# awk -F: '{print $1"#"$2"#"$3"#"$4"#"$5"|"$6","$NF}' /etc/passwd
## 修改输出后内容的分隔符
[root@m01 ~]# awk -F: -vOFS=# '{print $1,$2,$3,$4,$5,$6,$NF}' /etc/passwd
[root@m01 ~]# awk -F: '{print $0}' /etc/passwd
awk取行取列
## 取出top中的运行时间
[root@m01 ~]# top -n1 |awk 'NR==1{print $5}'
1
## 取出网卡配置文件的IP地址
[root@m01 ~]# awk -F= '/IPADDR/{print $2}' /etc/sysconfig/network-scripts/ifcfg-eth0
10.0.0.61
# 请找出姓氏是张的人,他们第二次捐款的数额及姓名
##### 有问题的写法 ############
[root@m01 ~]# cat user.txt
1 Zeng Laoshi 133411023 :110:100:75
2 Deng Ziqi 44002231 :250:10:88
3 Zhang Xinyu 877623568 :120:300:200
4 Gu Linazha 11029987 :120:30:79
5 Di Lireba 253097001 :220:100:200
6 Jiang Shuying 535432779 :309:10:2
7 Ju Jingyi 68005178 :130:280:385
8 Zhang Yuqi 376788757 :500:290:33
9 Wen Zhang 259872003 :100:200:300
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}/^Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}/Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300
Zhang Yuqi 290
Wen Zhang 200
############# 正确写法 ################
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}$2~/Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300
Zhang Yuqi 290
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}$2~/Zhang/ && $3~/X/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓名","捐款数额"}$2~/Zhang/ && $3~/X/{print $2 $3,$6}' user.txt |column -t
姓名 捐款数额
ZhangXinyu 300
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓名","捐款数额"}$2~/Zhang/ && $3~/X/{print $2 $3,$(NF-1)}' user.txt |column -t
姓名 捐款数额
ZhangXinyu 300
### 显示所有以25开头的QQ号及姓名
[root@m01 ~]# awk '$4~/^25/{print $2 $3,$4}' user.txt
DiLireba 253097001
WenZhang 259872003
### 显示所有QQ号最后一位是1或者3的人,全名及QQ
[root@m01 ~]# awk '$4~/1$|3$/{print $2$3,$4}' user.txt
ZengLaoshi 133411023
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
[root@m01 ~]# awk '$4~/(1|3)$/{print $2$3,$4}' user.txt
ZengLaoshi 133411023
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
[root@m01 ~]# awk '$4~/[13]$/{print $2$3,$4}' user.txt
ZengLaoshi 133411023
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
### 显示每个捐款值都以$开头 $110:$00$75
[root@m01 ~]# awk '{gsub(/:/,"$");print $0}' user.txt
1 Zeng Laoshi 133411023 $110$100$75
2 Deng Ziqi 44002231 $250$10$88
3 Zhang Xinyu 877623568 $120$300$200
4 Gu Linazha 11029987 $120$30$79
5 Di Lireba 253097001 $220$100$200
6 Jiang Shuying 535432779 $309$10$2
7 Ju Jingyi 68005178 $130$280$385
8 Zhang Yuqi 376788757 $500$290$33
9 Wen Zhang 259872003 $100$200$300
[root@m01 ~]# awk '{gsub(/:/,"$",$5);print $0}' user.txt
1 Zeng Laoshi 133411023 $110 :100:75
2 Deng Ziqi 44002231 $250 :10:88
3 Zhang Xinyu 877623568 $120 :300:200
4 Gu Linazha 11029987 $120 :30:79
5 Di Lireba 253097001 $220 :100:200
6 Jiang Shuying 535432779 $309 :10:2
7 Ju Jingyi 68005178 $130 :280:385
8 Zhang Yuqi 376788757 $500 :290:33
9 Wen Zhang 259872003 $100 :200:300
function gsub(){
$1
$2
$3
xxxx
}
gsub(xx,aaa,d)
gsub("被替换的内容","替换的新内容")
gsub("被替换的内容","替换的新内容",第N列)
## 综合应用:找出ifconfig中范围是1-255的数字
[root@m01 ~]# ifconfig |awk -vRS='[^0-9]+' '$0>=1 && $0<=255'
10
61
255
255
255
10
255
6
80
20
29
3
64
20
29
3
2
5
2
6
1
172
16
1
61
255
255
255
172
16
1
255
6
80
20
29
3
9
64
20
29
3
9
46
9
117
29
7
73
127
1
255
6
1
128
10
awk模式与动作
awk -F: 'NR==1{print $1,$3}' /etc/passwd
上面这条命令我们可以看到,'NR==1{print $1,$3}'
可以理解为,'模式{动作}'
== '条件{指令}'
awk中的模式
- 正则表达式
# 正则表达式写法
'/正则表达式/flag'
'$1~/正则表达式/flag'
'$1!~/正则表达式/flag'
只不过我们在awk中很少使用flag
- 比较表达式
NR==1
NR>=10
NR<=100
NR>=1 && NR<=10
$1>=100
- 范围模式
## 精确匹配行号:从第10行到第20行
NR==10,NR==20
## 精确匹配字符串:从该字符串的行到另一个字符串所在行
'/root/,/zls/'
'/从哪个字符串所在行/,/到那个字符串所在行/' #中间的行都包含进去
## 模糊匹配字符串:从含有该字符串所在行到含有另一字符串所在行
'$1~/oo/,$1~/zl/'
- 特殊模式
BEGIN
END
awk中的动作
在awk中,我们最常用的动作就是 print
当然我们还有别的动作可以使用:
- print打印
- gsub替换
- 变量赋值
- 统计计算
useradd name;pass=`echo $RANDOM|md5sum|cut -c 1-10`;echo $pass|passwd --stdin name;echo $pass:$user >> /tmp/user.txt
seq 100|awk '{print "useradd test"$1";pass=`echo $RANDOM|md5sum|cut -c 1-10`;echo $pass|passwd --stdin test"$1";echo $pass:test"$1" >> /tmp/user.txt"}'|bash
如果要使用BEGIN模式,那么一定要成双成对的出现:BEGIN{}
那么我们要知道,BEGIN{}中,大括号里面的内容,会在读取文件内容之前执行
主要应用场景:
- 1.计算
[root@zabbix01 ~]# awk 'BEGIN{print 1/3}'
0.333333
- 2.awk功能测试
- 3.输出表格的表头
END模式
一般来说,END{}要比BEGIN{}重要一些,BEGIN{}可有可无,计算其实可以放在读取文件的时候,也可以执行
END{}中,大括号里面的内容,会在awk读取完文件的最后一行后,进行处理
作用:一般我们使用END{}来显示对日志内容分析后的一个结果
当然,还有其他功能,比如文件读取完了,可以显示一些尾部信息
# 1.统计/etc/service文件中一共有多少行
[root@m01 ~]# awk '{hang++;print hang}' /etc/services
1
...
11176
不需要过程,只要结果
[root@m01 ~]# awk '{hang++}END{print hang}' /etc/services
11176
### 只能统计文件的所有行数
[root@m01 ~]# awk 'END{print NR}' /etc/services
11176
### 流氓写法
[root@m01 ~]# sed -n '$=' /etc/services
11176
[root@m01 ~]# grep -c '.*' /etc/services
11176
[root@m01 ~]# wc -l /etc/services
11176 /etc/services
# 2.统计/etc/service中空行的数量
[root@m01 ~]# awk '/^$/{print}' /etc/services
[root@m01 ~]# awk '/^$/{i++}END{print i}' /etc/services
17
# 3.统计出下列文件中所有人的年龄和
### 脚本方式
#!/usr/bin/bash
n=0
for line in `cat user.txt`;do
if [[ $line =~ [0-9]+ ]];then
((n+=$line))
fi
done
echo $n
### awk方式
[root@m01 ~]# cat user.txt
姓名 年龄
曾老湿 23
苍颈空 18
西冶详 99
[root@m01 ~]# awk 'NR>1{print $2}' user.txt
23
18
99
[root@m01 ~]# awk 'NR>1{n+=$2}END{print n}' user.txt
140
# 4.统计nginx日志中,状态码是200的次数以及,状态码是200时占用的流量
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz |awk 'BEGIN{print "状态码200的次数","总流量"}$10~/200/{code++;byte+=$11}END{print code,byte}'|column -t
状态码200的次数 总流量
3100 190477111
脚本写法:
awk '
BEGIN{
print "状态码200的次数","总流量"
}
$10~/200/{
code++;byte+=$11
}
END{
print code,byte
}'
# 5.统计nginx日志中状态码是4xx和5xx的次数及总流量
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz|awk '$10~/^[45]/{i++;n+=$11}END{print i,n}'
580 519243
# 6.综合应用:分别统计每种状态码的次数和每个状态码的总流量
zcat blog.driverzeng.com_access.log-20220623.gz |awk '
BEGIN{
print "状态码","总流量"
}
$10~/200/{
i1++;n1+=$11
}
$10~/^3/{
i2++;n2+=$11
}
$10~/^4/{
i3++;n3+=$11
}
$10~/^5/{
i4++;n4+=$11
}
END{
print "200次数:"i1,"200的流量:"n1
print "3xx次数:"i2,"3xx的流量:"n2
print "4xx次数:"i3,"4xx的流量:"n3
print "5xx次数:"i4,"5xx的流量:"n4
}'|column -t
awk数组
在awk中的数组数据类型,是非常好用的一个类型,不像是shell,当然shell中数组也有它自己的优点。
awk中的数组,专门用来统计不同的分类。
** 例如:**
1.nginx日志中每个IP出现的次数
2.nginx日志中每种状态码出现的次数
3.nginx日志中每个URI的访问次数
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz|awk '{print $1}'|sort|uniq -c|sort -nr
awk数组赋值
[root@m01 ~]# awk 'BEGIN{array[0]="zls";array[1]="wyk"}'
awk数组取值
[root@m01 ~]# awk 'BEGIN{array[0]="zls";array[1]="wyk";print array[0],array[1]}'
zls wyk
shell中循环数组
array[0]='zls'
array[1]='cls'
for name in ${array[*]};do
echo $name
done
awk循环数组
for(条件){
动作
}
for 条件;do
动作
done
[root@m01 ~]# awk 'BEGIN{array[0]="zls";array[1]="wyk";for(num in array){print num}}'
0
1
[root@m01 ~]# awk 'BEGIN{array[0]="zls";array[1]="wyk";for(num in array){print array[num]}}'
zls
wyk
## 统计nginx日志中的每一个IP地址访问的次数
zcat blog.driverzeng.com_access.log-20220623.gz |awk '{array[$1]++}END{for(ip in array){print ip,array[ip]}}'
#1.取出下列域名并根据域名,进行统计排序处理
https://blog.driverzeng.com/index.html
https://blog.driverzeng.com/1.html
http://post.driverzeng.com/index.html
http://mp3.driverzeng.com/index.html
https://blog.driverzeng.com/3.html
http://post.driverzeng.com/2.html
[root@m01 ~]# awk -F/ '{domain[$3]++}END{for(name in domain){print name,domain[name]}}' 1.txt
blog.driverzeng.com 3
post.driverzeng.com 2
mp3.driverzeng.com 1
#2.统计nginx日志中,每个IP访问使用的流量总和
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz |awk '{ip[$1]++;liuliang[$1]+=$11}END{for(i in ip){print i,ip[i],liuliang[i]}}'
awk的判断
awk判断与shell判断对比
## shell
if [ 条件 ];then
动作
fi
if [ 条件 ];then
else
fi
if [ 条件 ];then
elif [ 条件 ];then
else
fi
## awk
if(条件){
动作
}
if(条件){
动作
}else{
动作
}
if(条件){
动作
}else if(条件){
动作
}else{
动作
}
awk '{}END{for(条件){if(条件){动作}else if(条件){动作}else{动作}}}'
awk '{
读文件的动作
}END{
for(条件){
if(条件){
动作
}else if(条件){
动作
}else{
动作
}
}
}'
#1.判断磁盘使用率大于70%,大于显示磁盘空间不足,不大于显示正常
## 取出磁盘使用率
[root@m01 ~]# df -h|awk -F '[ %]+' 'NR==2{if($5>70){print "磁盘空间不足"}else{print "磁盘空间还行"}}'
磁盘空间还行
[root@m01 ~]# df -h|awk -F '[ %]+' 'NR==2{if($5>70){print "磁盘空间不足"}else{print "磁盘空间还行,当前磁盘使用率:"$5"%"}}'
磁盘空间还行,当前磁盘使用率:9%
### 作业:
# 1.从1加到100
#2.企业面试题:统计每个学生的总成绩和平均成绩
stu01 70 80 90 100 90 80
stu02 89 78 98 67 97 90
stu03 56 12 33 44 55 66 77
stu04 89 78 77 99 100 30
stu05 98 97 96 95 94 93
stu06 100 20 20 20 20 20
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了