目 录CONTENT

文章目录

Linux文本处理三剑客(grep sed awk)

yyzq
2022-09-16 / 0 评论 / 5 点赞 / 1,344 阅读 / 5,214 字

正则表达式
可以使用若干符号配合某工具对字符串进行增删改查操作

head -5 /etc/passwd > user    //准备素材

image-1663302140422

一、grep

grep ^root user    //找以root开头的行
grep bash$ user     //找以bash结尾的行
grep ^$ user   //找空行
grep -v ^$ user  //显示除了空行的内容
grep "[root]" user    //找r、o、t任意一个字符 
grep "[rot]" user    //效果同上
grep "[^rot]" user   //显示r或o或t以外的内容
grep "[0123456789]" user   //找所有数字
grep "[0-9]" user    //效果同上
grep "[^0-9]" user   //显示数字以外内容
grep "[a-z]" user    //找所有小写字母
grep "[A-Z]" user    //找所有大写字母
grep "[a-Z]" user    //找所有字母
grep "[^0-9a-Z]" user   //找所有符号
grep "." user   //找任意单个字符,文档中每个字符都可以理解为任意字符
grep "r..t" user  //找rt之间有2个任意字符的行
grep "r.t" user  //找rt之间有1个任意字符的行,没有匹配内容,就无输出
grep "*" user   //错误用法,*号是匹配前一个字符任意次,不能单独使用
grep "ro*t" user   //找rt,中间的o有没有都行,有几次都行
grep ".*" user  //找任意,包括空行 .与*的组合在正则中相当于通配符的效果
grep "ro\{1,2\}t" user  //找rt,中间的o可以有1~2个
grep "ro\{2,6\}t" user  //找rt,中间的o可以有2~6个
grep "ro\{1,\}t" user  //找rt,中间的o可以有1个以及1个以上
grep "ro\{3\}t" user   //找rt,中间的o必须只有有3个

image-1663302220642
以上命令均可以加-E选项并且去掉所有\,改成扩展正则的用法,比如

grep "ro\{1,\}t" user
可以改成 
grep -E "ro{1,}t" user    
或者
egrep "ro{1,}t" user   
grep "ro\{1,\}t" user   //使用基本正则找o出现1次以及1次以上
egrep "ro{1,}t" user   //使用扩展正则,效果同上,比较精简
egrep "ro+t" user   //使用扩展正则,效果同上,最精简
grep "roo\{0,1\}t" user   //使用基本正则找第二个o出现0~1次
egrep "roo{0,1}t" user   //使用扩展正则,效果同上,比较精简
egrep "roo?t" user   //使用扩展正则,效果同上,最精简
egrep "(0:){2}" user  //找连续的2个0:  小括号的作用是将字符组合为一个整体
egrep "root|bin" user  //找有root或者bin的行
egrep  "the\b" abc.txt   //在abc.txt文件中找the,右边不允许出现数字、字母、下划线
egrep  "\bthe\b" abc.txt   //两边都不允许出现数字、字母、下划线
egrep  "\<the\>" abc.txt   //效果同上

二、sed

流式编辑器
可以对文档进行非交互式增删改查,逐行处理
用法:

| sed 选项 条件 指令

sed 被处理文档

选项 -n 屏蔽默认输出 -r 支持扩展正则 -i 修改源文件

指令 p 输出 d 删除 s 替换

条件 行号 //

-e<script>或--expression=<script>:以选项中的指定的script来处理输入的文本文件;
-f<script文件>或--file=<script文件>:以选项中指定的script文件来处理输入的文本文件;
-h或--help:显示帮助;
-n或--quiet或——silent:仅显示script处理后的结果;
-V或--version:显示版本信息。

sed命令

a\ # 在当前行下面插入文本。
i\ # 在当前行上面插入文本。
c\ # 把选定的行改为新的文本。
d # 删除,删除选择的行。
D # 删除模板块的第一行。
s # 替换指定字符
h # 拷贝模板块的内容到内存中的缓冲区。
H # 追加模板块的内容到内存中的缓冲区。
g # 获得内存缓冲区的内容,并替代当前模板块中的文本。
G # 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
l # 列表不能打印字符的清单。
n # 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
N # 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
p # 打印模板块的行。
P # (大写) 打印模板块的第一行。
q # 退出Sed。
b lable # 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
r file # 从file中读行。
t label # if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
T label # 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
w file # 写并追加模板块到file末尾。  
W file # 写并追加模板块的第一行到file末尾。  
! # 表示后面的命令对所有没有被选定的行发生作用。  
= # 打印当前行号码。  
# # 把注释扩展到下一个换行符以前。

sed替换标记

g # 表示行内全面替换。  
p # 表示打印行。  
w # 表示把行写入一个文件。  
x # 表示互换模板块中的文本和缓冲区中的文本。  
y # 表示把一个字符翻译为另外的字符(但是不用于正则表达式)
\1 # 子串匹配标记
& # 已匹配字符串标记

sed元字符集

^ # 匹配行开始,如:/^sed/匹配所有以sed开头的行。
$ # 匹配行结束,如:/sed$/匹配所有以sed结尾的行。
. # 匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d。
* # 匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。
[] # 匹配一个指定范围内的字符,如/[sS]ed/匹配sed和Sed。  
[^] # 匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。
\(..\) # 匹配子串,保存匹配的字符,如s/\(love\)able/\1rs,loveable被替换成lovers。
& # 保存搜索字符用来替换其他字符,如s/love/ **&** /,love这成 **love** 。
\< # 匹配单词的开始,如:/\<love/匹配包含以love开头的单词的行。
\> # 匹配单词的结束,如/love\>/匹配包含以love结尾的单词的行。
x\{m\} # 重复字符x,m次,如:/0\{5\}/匹配包含5个0的行。
x\{m,\} # 重复字符x,至少m次,如:/0\{5,\}/匹配至少有5个0的行。
x\{m,n\} # 重复字符x,至少m次,不多于n次,如:/0\{5,10\}/匹配5~10个0的行。

sed用法实例
替换操作:s命令
替换文本中的字符串:

sed 's/book/books/' file

-n选项 和 p命令 一起使用表示只打印那些发生替换的行:

sed -n ‘s/test/TEST/p’ file

直接编辑文件 选项-i ,会匹配file文件中每一行的所有book替换为books:

sed -i 's/book/books/g' file

全面替换标记g
使用后缀 /g 标记会替换每一行中的所有匹配:

sed 's/book/books/g' file

当需要从第N处匹配开始替换时,可以使用 /Ng:

echo sksksksksksk | sed 's/sk/SK/2g'skSKSKSKSKSK
echo sksksksksksk | sed 's/sk/SK/3g'skskSKSKSKSK
echo sksksksksksk | sed 's/sk/SK/4g'skskskSKSKSK

定界符
以上命令中字符 / 在sed中作为定界符使用,也可以使用任意的定界符:

sed 's:test:TEXT:g'sed 's|test|TEXT|g'

定界符出现在样式内部时,需要进行转义:

sed 's/\/bin/\/usr\/local\/bin/g'

删除操作:d命令
删除空白行:

sed '/^$/d' file

删除文件的第2行:

sed '2d' file

删除文件的第2行到末尾所有行:

sed '2,$d' file

删除文件最后一行:

sed '$d' file

删除文件中所有开头是test的行:

sed '/^test/'d file
sed -n  'p'  user    //输出所有行
sed -n  '1p'  user   //输出第1行
sed -n  '2p'  user   //输出第2行
sed -n  '3p'  user    //输出第3行
sed -n  '2,4p'  user   //输出2~4行
sed -n  '2p;4p'  user   //输出第2行与第4行
sed -n '3,+1p' user   //输出第3行以及后面1行
sed -n '/^root/p' user   //输出以root开头的行  
sed -n '/root/p' user   //输出包含root的行
sed -nr '/^root|^bin/p' user   //输出以root开头的行或bin开头的行,|是扩展正则,需要r选项
sed -n '1!p' user   //输出除了第1行的内容,!是取反
sed -n '$p'  user  //输出最后一行
sed -n '='  user   //输出行号,如果是$=就是最后一行的行号

以上操作,如果去掉-n,在将p指令改成d指令就是删除
输出所有行 sed -n ‘p’ abc.txt

输出第4行 sed -n ‘4p’ abc.txt

输出第4~7行 sed -n ‘4,7p’ abc.txt

输出以bin开头的行 sed -n ‘/^bin/p’ abc.txt

输出文件的总行数 sed -n ‘$=’ abc.txt

删除第3~5行 sed ‘3,5d’ abc.txt

删除所有包含xml的行 sed ‘/xml/d’ abc.txt

删除不包含xml的行 sed ‘/xml/!d’ abc.txt

删除以install开头的行 sed ‘/^install/d’ abc.txt

删除文件的最后一行 sed ‘$d’ abc.txt

删除所有空行 sed ‘/^$/d’ abc.txt
替换

sed 's/2017/6666/' shu.txt   //把所有行的第1个2017替换成6666
sed 's/2017/6666/2' shu.txt  //把所有行的第2个2017替换成6666
sed '1s/2017/6666/' shu.txt  //把第1行的第1个2017替换成6666
sed '3s/2017/6666/3' shu.txt  //把第3行的第3个2017替换成6666
sed 's/2017/6666/g' shu.txt   //所有行的所有个2017都替换
sed '/2024/s/2017/6666/g' shu.txt  //找含有2024的行,将里面的所有2017替换成6666

如果想把 /bin/bash 替换成 /sbin/sh 怎么操作?

sed -i '1s/bin/sbin/' user   //传统方法可以一个一个换,先换一个
sed -i '1s/bash/sh/' user   //再换一个

如果想一起一步替换:

sed 's//bin/bash//sbin/sh/' user     //直接替换,报错
sed 's/\/bin\/bash/\/sbin\/sh/' user   //使用转义符号可以成功,但不方便
sed 's!/bin/bash!/sbin/sh!' user   //最佳方案,更改s的替换符
sed 's(/bin/bash(/sbin/sh(' user   //替换符号可以用所有数字键上的

sed其他指令补充

a行下追加 i行上添加 c替换整行

sed 'a 666' user    //所有行的下面追加666
sed '1a 666' user   //第1行的下面追加666
sed '/^bin/a 666' user   //在以bin开头的行的下面追加666
sed 'i 666' user  //所有行的上面添加666
sed '5i 666' user   //第5行的上面添加666
sed '$i 666' user  //最后1行的上面添加666
sed 'c 666' user   //所有行都替换成666
sed '1c 666' user  //替换第1行为666

三、awk

awk

可以实现精确搜索并输出 ,逐行处理

1,前置指令 | awk 选项 条件 指令

2,awk 选项 条件 指令 被处理文档

选项 -F 定义分隔符

指令 print

条件 /字符串/

内置变量 $1第一列 $2第二列 $3第三列 …

$0 所有列 NR行号 NF 列号

[root@proxy opt]# cat abc.txt   //准备素材


hello the world
welcome to beijing
awk '{print}' abc.txt    //输出所有
awk '/to/{print}' abc.txt    //输出有to的那行
awk '{print $2}' abc.txt    //输出所有行的第2列
awk '/to/{print $1}' abc.txt    //输出有to的那行的第1列
awk '{print $0}' abc.txt    //输出所有行所有列
awk '{print $0,$1}' abc.txt  //输出所有行所有列,第1列
awk '{print NR}' abc.txt   //输出所有行的行号
awk '{print NR,$0}' abc.txt   //输出所有行的行号,所有列
awk '{print NR,NF}' abc.txt   //输出所有行的行号,列号(有几列)
awk '/^bin/{print NR}'  user    //找以bin开头的行,显示该行的行号
awk '/^bin/{print NR,$0}'  user   //找以bin开头的行,显示该行的行号,所有列
awk '{print NF}'   user   //输出所有行的列号(每行有几列)
awk -F: '{print $1}'  user   //文档中如果没有空格,可以用F修改分隔符
awk -F: '{print $1,$6}'  user    //使用冒号作为列的分隔符,显示第1、6列
awk -F: '{print $1" 的家目录是 "$6}'  user    //还可以输出常量,加双引号即可
awk -F: '{print $1" 的解释器是 "$7}'  user

收集根分区剩余容量

df -h | awk  '/\/$/{print  $4}'    //使用df  -h 作为前置指令交给awk处理找到以/结尾的行,并输出第4列
df -h | awk  '/\/$/{print  "根分区剩余容量是"$4}'   //然后加常量输出

收集网卡流量信息

ifconfig eth0 | awk '/RX p/{print "eth0网卡接收的数据量是"$5"字节"}'
ifconfig eth0 | awk '/TX p/{print "eth0网卡发送的数据量是"$5"字节"}'

awk的条件

1,/字符串/ 还可以使用正则 ~ 包含 !~不包含

awk -F: '$6~/root/{print}'  user  //输出第6列包含root的行
awk -F: '$6~/bin/{print}'  user   //输出第6列包含bin的行
awk -F: '$6!~/bin/{print}'  user  //输出第6列不包含bin的行

2,使用数字或者字符串

== != > >= < <=

awk -F: '$3<3{print}' user   //输出第3列小于3的行
awk -F: '$3<=3{print}' user   //输出第3列小于等于3的行
awk -F: 'NR==2{print}' user   //输出第2行
awk -F: 'NR>2{print}' user    //输出行号大于2的行

3,逻辑组合 &&并且 ||或者

awk -F: 'NR==2||NR==4{print}' user   //找行号是2或者4的行
awk -F: 'NR==2||NR==40{print}' user   //如果只有一个条件满足就显示一个
awk -F: '$7~/bash/&&$3<=500{print}' user   //找第7列包含bash并且第3列小于等于500的

awk 'NR==2&&NR==4{print}' user   //找行号既是2又是4的行,不存在,无输出
awk -F: '$7~/bash/&&NR<=3{print}' user  //找第7列包含bash并且行号是1~3的
awk -F: '$7~/bash/||NR<=3{print}' user  //找第7列包含bash或者行号是1~3的


awk -F: '$1~/root/' user    //找第1列包含root的行如果有用户叫root6,也会搜到,比较宽松的搜索方式,如果任务就是{print}的话可以省略不写

awk -F: '$1=="root"' user  //找第1列完全等于root的行多一个字符少一个字符都不行,比较严格的搜索方式

4,运算

awk 'NR%2==0{print NR,$0}' user   //在条件中使用运算,找到将行号除以2余数等于0的行,然后输出该行的行号和所有列,相当于输出偶数行

awk处理时机,可以执行额外任务
BEGIN任务 执行1次,读取文档之前执行
逐行任务 执行n次,读取文档时执行
END任务 执行1次,读取文档之后执行
BEGIN{ 任务1 }{ 任务2 }END

awk -F: 'BEGIN{print "ok"}{print $1}END{print "ok"}' user
awk 'BEGIN{print NR}{print NR}END{print NR}' user

利用awk处理时机,输出下列内容
image-1663341365034

awk 'BEGIN{print "User\tUID\tHome"}'   //第1步输出表头信息
awk -F: '{print $1"\t"$3"\t"$6}' user    //第2步输出内容
awk 'END{print "总计"NR"行" }' user    //第3步输出结尾
 
awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1"\t"$3"\t"$6}END{print "总计"NR"行"}' user   //合在一起写

使用awk统计网站访问量

setenforce 0    //关闭selinux
systemctl stop firewalld  //关闭防火墙
systemctl restart httpd  //开启网站服务

使用浏览器多访问几次网站,包括本机用curl

curl  192.168.4.7:82    //如果端口没改过就不用敲
awk  '{print $1}'  /var/log/httpd/access_log   //初步统计,不完美
使用awk数组+for循环实现高级搜索

数组 相当于可以存储多个值的特殊变量
数组名[下标]=下标对应的值

awk 'BEGIN{a[1]=10;a[2]=20;print a[2],a[1]}'    //使用awk测试数组,首先创建数组a,下标1对应值是10,下标2对应值是20,然后输出下标是2与下标是1的值
awk 'BEGIN{a["abc"]="abcabc";a["xyz"]="xyzxyz";print a["xyz"]}'   //数组的下标和值都可以不是数字,测试时加双引号即可

以上信息是手工输入,实际情况中通常是从文档收集
准备一个文档,里面有6行,每行分别是abc、xyz、abc、opq、xyz、abc 然后
按照awk逐行处理的工作特点使用awk ‘{a[$1]++}’ shu.txt 走完每一行得到下列结果
但不会输出到屏幕

a[$1]++    a[abc]++   a[abc]=1
a[$1]++    a[xyz]++   a[xyz]=1
a[$1]++    a[abc]++   a[abc]=2
a[$1]++    a[opq]++   a[opq]=1
a[$1]++    a[xyz]++   a[xyz]=2
a[$1]++    a[abc]++   a[abc]=3

如果要输出到屏幕可以使用命令awk ‘{a[$1]++}END{print a[“abc”]}’ shu.txt

根据上述操作得知使用数组可以收集信息,但收集完了之后查看确不方便,可以用for循环实现。方法如下:
for(变量名 in 数组名){print 变量名} //这个格式可以查看数组的所有下标

awk '{a[$1]++}END{for(i in a){print i,a[i]}}' shu.txt   //使用逐行任务与数组收集文档shu.txt中的信息,然后在END任务中使用for循环显示所有数组a的下标与值
 
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log   //将上述的文件替换成网站的日志,就可以最终用来查看日志得到可以得到哪个ip来访以及来访的次数
 
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log | sort  -nr  -k  2    //使用sort命令增加排序功能,-n是以数字形式排序,-r是降序, -k是指定为第几列排序

/var/log/secure是安全日志,如果有人登陆时输入错误密码的话
信息会记录下来,这种信息可以用awk抓取出来,方法如下:

awk '/Failed password for root/{ip[$11]++}END{for(i in ip){print i,ip[i]}}'  /var/log/secure    //统计安全日志中访问root账户密码输入错误的ip地址与次数

编写脚本,可以查看系统的各种参数信息

#!/bin/bash
while :
do
clear
free -h | awk  '/^Mem:/{print "剩余内存容量是"$4}'
df -h | awk  '/\/$/{print "根分区剩余容量是"$4}'
awk 'END{print "用户总数是"NR"个"}'  /etc/passwd
who | awk 'END{print "登录用户数量是"NR"个"}'
uptime | awk '{print "cpu的15分钟平均负载是"$NF}'
rpm -qa | awk 'END{print "安装的软件包数量是"NR"个"}'
sleep 3
done
5

评论区