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:递归检索文件中的内容
-R:递归检索文件中的内容,包括软链接文件
-l:只显示文件名
-h:只显示文件内容
-f:对比文件内容,内容少的文件在前面,内容多的文件在后面,取反可以看到不同的文件内容
-c:统计行数,类似于 wc -l
-G:支持基础正则 bre
-m:显示前N行 类似于 head -n
#检索文件中的内容
[root@m01 web]# grep -r 'www.lw.com' /root/web/
/root/web/css/style.css:www.lw.com
/root/web/js/main.js:www.lw.com
/root/web/index.html:www.lw.com
##检索文件中的内容只显示文件名并替换内容
[root@m01 web]# grep -lr 'www.lw.com' /root/web/|xargs sed -i.lw
's#www.lw.com#www.baidu.com#g'
sed命令
在sed中,我们核心的内容,主要分为四个部分:
- 增
- 删
- 改
- 查
当然我们还有一些进阶内容:模式空间与保持空间
sed命令执行流程
举个例子
# 以下是lw.txt文件内容
1,lw,666
2,wls,777
3,cls,888
4,lls,999
执行 sed -n '3p' lw.txt 命令后,sed都做了啥?
- sed先是按行读取文件内容
- 每读取一行内容,都会进行一次判断,是不是你想要的行
- 如果不是,则判断是不是 加了-n选项
- 如果加了-n,就读取下一行
- 如果没加-n,就会将所有内容输出到命令行(默认输出)
- 如果是,你想 要的那一行(第三行)则判断执行的后续动作(p d s a i c)
- 动作处理完成后,输出指定的内容
- 即便是读取完 了,内容也输出了,sed也会继续往后读,直到文件的最后一行
sed - 查
#p:打印,显示
## sed显示单行内容
[root@m01 ~]# sed '3p' lw.txt
1,lw,666
2,wls,777
3,cls,888
3,cls,888
4,lls,999
## sed显示单行内容
[root@m01 ~]# sed '3p' lw.txt
1,zls,666
2,wls,777
3,cls,888
3,cls,888
4,lls,999
## sed显示多行内容并取消默认输出
[root@m01 ~]# sed -n '1,3p' lw.txt
1,lw,666
2,wls,777
3,cls,888
## sed模糊查询
[root@m01 ~]# sed -n '/lw/p' lw.txt
1,lw,666
[root@m01 ~]# sed -nr '/lw|cls/p' lw.txt
1,lw,666
3,cls,888
#打印lw到cls之间的内容
[root@m01 ~]# sed -n '/lw/,/cls/p' lw.txt
1,lw,666
2,wls,777
3,cls,888
## sed实现 grep -A
[root@m01 ~]# sed -n '/lw/,+2p' lw.txt
1,lw,666
2,wls,777
3,cls,888
[root@m01 ~]# grep 'lw' -A 2 lw.txt
1,lw,666
2,wls,777
3,cls,888
## sed 隔指定行数读取一行
[root@m01 ~]# sed -n '1~2p' lw.txt
1,lw,666
3,cls,888
[root@m01 ~]# sed -n '1~3p' lw.txt
1,lw,666
4,lls,999
sed - 删
d:delete 删除
i: 执行到源文件
## 删除指定行数,不修改原文件
[root@m01 ~]# sed '2d' lw.txt
## 删除最后一行并修改源文件
[root@m01 ~]# sed -i '$d' lw.txt
1,zls,666
3,cls,888
## 包含zls的行到cls的行都删掉
[root@m01 ~]# sed -n '/lw/,/cls/d' lw.txt
sed - 增
#c:replace 替换整行内容
[root@m01 ~]# cat lw.txt
1,lw,666
3,cls,888
4,lls,999
[root@m01 ~]# sed '2c2,rowey,234' lw.txt
1,lw,666
2,rowey,234
4,lls,999
a:append 追加
#追加一行至末尾
[root@m01 ~]# sed '$a5,rowey,234' lw.txt
1,lw,666
3,cls,888
4,lls,999
5,rowey,234
#追加一行至第二行后
[root@m01 ~]# sed '2a5,rowey,234' lw.txt
1,lw,666
3,cls,888
5,huanglong,438
4,lls,999
#追加一行至第一行后
[root@m01 ~]# sed '1a2,rowey,234' lw.txt
1,lw,666
2,rowey,234
3,cls,888
4,lls,999
#i:insert 插入
#插入最后一行至最后一行前
[root@m01 ~]# sed '$i2,rowey,234' lw.txt
1,lw,666
3,cls,888
2,rowey,234
4,lls,999
#插入一行至第一行前
[root@m01 ~]# sed '1i2,rowey,234' zls.txt
2,rowey,234
1,lw,666
3,cls,888
4,lls,999
sed - 改
s:substitute 替换
g:global 全局
s###g
s@@@g
s啥都行g
## 基础用法
[root@zabbix01 ~]# sed 's#lw#LW#g' lw.txt
1,LW,666
2,wls,777
3,cls,888
4,lls,999
## 使用正则
[root@zabbix01 ~]# sed 's#[0-9]#666#g' lw.txt
666,lw,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其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
awk 是一种很棒的语言,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。在 linux 系统日常处理工作中,发挥很重要的作用,掌握了 awk将会使你的工作变的高大上。 awk 是三剑客的老大,利剑出鞘,必会不同凡响
awk内置执行流程
三个阶段
1.读取文件前
BENGIN{}
1.读取文件之前,先看命令的选项,例如-F,-v
2.如果写了BEGIN{}则执行先在BEGIN{}中的指令
2.读取文件时
{}
1.awk在读取文件时,也是一行一行的读
2.读取一行之后,判断是否满足条件,如果是,则执行{对应动作}
3.如果不满足条件,awk继续读取下一行,直到满足条件或者到文件的最后一行
3.读取文件之后
END{}
1.所有文件读取完成之后,走END{}中的指令
实例
[root@web01 ~]# awk 'BEGIN{xxx}{print $1}END{print 1/3}' lw.txt
[root@web01 ~]# awk -F: 'BENGIN{print "name","uid"}{print $1,$3}END{print"文件处理完成"}' /etc/passwd|column -t
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
rpc 32
rpcuser 29
nfsnobody 65534
ntp 38
文件处理完成
awk的行与列
awk中的行与列与数据库中的一样,被称为记录与字段
行: 记录 record
列: 字段 field
awk内置变量
awk内置变量 | 变量含义 | awk选项 | 选项含义 | awk动作 | 动作含义 |
---|---|---|---|---|---|
NR | Number of Record 行号 | -F | 指定分隔符 | gsub | 替换 |
RS | Record Separator 行的分隔符(\n) | -v | 指定变量(内置变量、自定义变量) | 打印 | |
FS | Field Separator 列的分隔符(空格) | ||||
NF | Number Of Filed 每一行有多少列 |
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
## 包含lls和cls的行
[root@m01 ~]# awk '/zls|cls/' lls.txt
1,lls,666
3,cls,888
lls,111
#大于等于第三行的内容
[root@m01 ~]# awk 'NR>=3' lls.txt
/tmp/check_2.txt
/tmp/check_3.txt
/tmp/check_4.txt
/tmp/check_5.txt
3,cls,888
lls,111
4,lls,999
#取lls到cls的内容,因为第二个lls匹配不到cls所以会将下面内容全部打印
[root@m01 ~]# awk '/lls/,/cls/' lls.txt
1,lls,666
/tmp/check_1.txt
/tmp/check_2.txt
/tmp/check_3.txt
cls
lls
4,wls,999
cls
### awk的控制结束标记
Record Separator
[root@m01 ~]# awk -vRS=, 'NR==1' lls.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 Luo 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
姓 名 捐款数额
Zhang Xinyu 300
Zhang Yuqi 290
Wen Zhang 200
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}/^Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
############# 正确写法 ################
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}$2~/Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300
Zhang Yuqi 290
##扩展
#以空格冒号为分隔符,$2包含Zhang并且$3包含X
[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
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
[root@m01 ~]# awk '$4~/(1|3)$/{print $2$3,$4}' user.txt
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
[root@m01 ~]# awk '$4~/[13]$/{print $2$3,$4}' user.txt
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
### 显示每个捐款值都以$开头 $110:$00$75
[root@m01 ~]# awk '{gsub(/:/,"$");print $0}' user.txt
1 Luo 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 Luo Laoshi 1627556442 $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
gsub("被替换的内容","替换的新内容")
gsub("被替换的内容","替换的新内容",第N列)
综合应用:找出ifconfig中范围是1-255的数字
## 综合应用:找出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/,/lw/'
'/从哪个字符串所在行/,/到那个字符串所在行/' #中间的行都包含进去
## 模糊匹配字符串:从含有该字符串所在行到含有另一字符串所在行
'$1~/oo/,$1~/lw/'
- 特殊模式
BEGIN
END
在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{}
那么我们要知道,BEGIN{}中,大括号里面的内容,会在读取文件内容之前执行
主要应用场景:
- 计算
[root@zabbix01 ~]# awk 'BEGIN{print 1/3}'
0.333333
- awk功能测试
- 输出表格的标头
END模式
一般来说,END{}要比BEGIN{}重要一些,BEGIN{}可有可无,计算其实可以放在读取文件的时候,也可以执行
END{}中,大括号里面的内容,会在awk读取完文件的最后一行后,进行处理
作用:一般我们使用END{}来显示对日志内容分析后的一个结果
当然,还有其他功能,比如文件读取完了,可以显示一些尾部信息
blog.access.log-20220623
脚本写法:
awk '
BEGIN{
print "状态码200的次数","总流量"
}
$10~/200/{
code++;byte+=$11
}
END{
print code,byte
}'
# 1.统计/etc/service文件中一共有多少行
[root@m01 ~]# awk '{num++;print num}' /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.统计出下列文件中所有人的年龄和
[root@m01 ~]# cat user.txt
姓名 年龄
罗炜 23
苍颈空 18
西冶详 99
### 脚本方式
#!/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 ~]# 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]="lw"}'
awk数组取值
[root@m01 ~]# awk 'BEGIN{array[0]="zls";array[1]="lw";print array[0],array[1]}'
zls lw
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]="lw";for(num in array){print num}}'
0
1
[root@m01 ~]# awk 'BEGIN{array[0]="zls";array[1]="lw";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
# awk 'BEGIN{{i=1;do{print sum+=i;i++}while(i<=100)}}'| tail -1
5050
2.企业面试题:统计每个学生的总成绩和平均成绩
[root@m01 ~]# cat cj.txt
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
[root@m01 ~]# awk '{sum=0;for(i=2;i<=NF;i++)sum+=$i;print sum,sum/(NF-1)}' cj.txt
510 85
519 86.5
343 49
473 78.8333
573 95.5
200 33.3333
Comments | NOTHING