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 指定变量(内置变量、自定义变量) print 打印
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
posted @   Gabydawei  阅读(346)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
点击右上角即可分享
微信分享提示