Linux三剑客
Linux 中最重要的三个命令在业界被称为“三剑客”,它们是 grep,sed,awk。
我们知道 Linux 下一切皆文件,对 Linux 的操作就是对文件的处理,那么怎么能更好的处理文件呢?这就要用到我们的三剑客命令。
- grep :过滤文本
- sed : 修改文本
- awk : 处理文本
使用这三个工具可以提升运维效率,熟练掌握好正则表达式是使用Linux三剑客
的前提,在说三剑客前我们要插入一个小插曲就是“正则表达式”。在掌握好正则表达式后,将具体讲解三剑客的用法。
正则表达式
正则表达式:REGular EXPression, REGEXP。我们通过特定的字符串匹配模板,来获取到所需的内容。
Linux 三剑客以正则表达式作为基础,而在 Linux 系统中,支持两种正则表达式:
- 标准正则表达式
- 扩展正则表达式
标准正则表达式:
^ #以某字符开头
$ #以某字符结尾
. #匹配除换行符之外的任意单个字符
* #匹配前导字符的任意个数
[] #某组字符串的任意一个字符
[^] #取反
[a-z] #匹配小写字母
[A-Z] #匹配大写字母
[a-zA-Z] #匹配字母
[0-9] #匹配数字
\ #取消转义
() #分组
\n #代表第n个分组
扩展正则表达式:
{} #匹配的次数
{n} #匹配n次
{n,} #至少匹配n次
{n,m} #匹配 n 到 m 次
{,m} #最多匹配m次
+ #匹配至少有一个前导字符
? #匹配一个或零个前导字符
| #或
linux 三剑客之 grep
- 文本过滤器(根据文本内容过滤文件)
- grep 命令家族由 grep, egrep, fgrep 三个子命令组成,适用于不同的场景。具体如下:
- 命令描述
- grep 原生的 grep 命令,使用“标准正则表达式”作为匹配标准。
- egrep 扩展的 grep 命令,相当于
$(grep -E)
,使用“扩展正则表达式”作为匹配标准。 - fgrep 简化版的 grep 命令,不支持正则表达式,但搜索速度快,系统资源使用率低。
- 命令描述
语法格式:
grep [参数] [匹配规则] [操作对象]
参数:
-n #过滤文本时,将过滤出来的内容在文件内的行号显示出来
-A #匹配成功之后,将匹配行的后n行显示出来
-B #匹配成功之后,将匹配行的前n行显示出来
-C #匹配成功之后,将匹配行的前后各n行显示出来
-c #只显示匹配成功的行数
-o #只显示匹配成功的内容
-v #显示不包含匹配文本的所有行(反向过滤)
-q #静默输出(禁止输出任何结果,已退出状态表示搜索是否成功)
-i #搜索时,忽略大小写
-l #匹配成功之后,将文本的名称打印出来
-h #查询多文件时不显示文件名
-b #打印匹配行距文件头部的偏移量,以字节为单位
-s #不显示不存在、没有匹配文本的错误信息
-w #匹配整词
-x #匹配整行
-R|-r #递归匹配
-E #使用扩展正则,等价于 egrep
知识储备:
$? #上一行命令执行的结果,0代表执行成功,其他数字代表执行失败。
wc #匹配行数
参数:
-l #打印匹配行数
-c #打印匹配的字节数
案例:
# 在/etc目录下,有多少个文件包含root。
[root@localhost ~]# grep -rl 'root' /etc/ | wc -l
130
# 在/etc/passwd文件中,匹配以ftp开头的行
[root@localhost ~]# grep '^root' /etc/passwd
root❌0:0:root:/root:/bin/bash
# 在/etc/passwd文件中,匹配以bash结尾的行,-n显示行号
[root@localhost ~]# grep -n 'bash$' /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
21:test:x:1001:1001::/home/test/:/bin/bash
22:gf:x:1002:1002::/home/gf:/bin/bash
28:tony:x:1004:1004::/home/tony:/bin/bash
29:user:x:1006:1001::/home/user:/bin/bash
# 匹配本机中有哪些ip
[root@localhost ~]# ip a | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}'
127.0.0.1
192.168.15.100
192.168.15.255
# 要求将/etc/fstab中的去掉包含 # 开头的行,且要求 # 后至少有一个空格
[root@localhost ~]# grep -vE '^#\ +' /etc/fstab
#
#
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=9f8a98b0-805c-4adf-b9ef-517a2b527f89 /boot xfs defaults 0 0
# 找出文件中至少有一个空格的行
[root@localhost ~]# grep -E '\ +' 1.txt
11 11
22 22
5 5 5 5
# 将 nginx.conf 文件中以#开头的行和空行,全部删除
[root@localhost ~]# grep -vE '^\ *#|^$' /etc/nginx/nginx.conf
linux 三剑客之 sed
sed 是一个流式编辑器,在处理行内容时功能十分强大。
定位到某一行,将某一行的某一部分给替换掉
定位到某一行,然后删除
定位到某一行,在该行后添加新的配置
语法格式
sed [参数] '处理规则' [操作对象]
参数
-n #取消默认输出
-e #允许多项编辑
-i #直接编辑源文件(把流向屏幕的内容写到文件中)
-r #支持扩展正则
-f #指定sed匹配规则脚本文件
定位
# 1、行号定位法,指定行号定位
# 不写代表定位所有行
[root@localhost ~]# sed '' 1.txt
1111
2222
3333
4444
5555
# 3定位到第三行
[root@localhost ~]# sed '3d' 1.txt
1111
2222
4444
5555
# 2,3从第二行到第三行
[root@localhost ~]# sed '2,3d' 1.txt
1111
4444
5555
# 2、正则定位法,指定正则定位。
[root@localhost ~]# sed '' 2.txt
gen111
222gen
333gen333
444xxx444
555gen555gen
# 删除包含gen的行
[root@localhost ~]# sed '/gen/d' 2.txt
444xxx444
# 删除以gen开头的行
[root@localhost ~]# sed '/^gen/d' 2.txt
222gen
333gen333
444xxx444
555gen555gen
# 删除以gen结尾的行
[root@localhost ~]# sed '/gen$/d' 2.txt
gen111
333gen333
444xxx444
# 3、数字+正则定位法
# 把1-3行的gen换成GEN
[root@localhost ~]# sed '1,3s/gen/GEN/' 2.txt
GEN111
222GEN
333GEN333
444xxx444
555gen555gen
# 把所有的gen换成GEN
[root@localhost ~]# sed 's/gen/GEN/g' 2.txt
GEN111
222GEN
333GEN333
444xxx444
555GEN555GEN
# 把所有的gen换成fen执行到文件中
[root@localhost ~]# sed -i 's/gen/fen/g' 2.txt
[root@localhost ~]# cat 2.txt
fen111
222fen
333fen333
444xxx444
555fen555fen
sed的编辑模式:
d :删除
p :打印
a : 在当前行后添加一行
c :用新文本修改(替换)当前行
i : 在当前行之前,插入文本(单独使用时)
r : 在文件中读内容
w : 将指定行写入文件
y : 将字符转换成另一个字符
s : 将字符串转换成另一个字符串(每一行只替换一次)
g : 全部执行
i : 忽略大小写(跟 s 模式一起使用时)
& :代表前面匹配到的内容
示例:
# a模式:在第二行后面添加一行xxx
[root@localhost ~]# sed '2axxx' 2.txt
fen111
222fen
xxx
333fen333
444xxx444
555fen555fen
# c模式:将第一行的内容替换为xxx
[root@localhost ~]# sed '1cxxx' 2.txt
xxx
222fen
333fen333
444xxx444
555fen555fen
# i模式:在第五行之前插入xxx
[root@localhost ~]# sed '5ixxx' 2.txt
fen111
222fen
333fen333
444xxx444
xxx
555fen555fen
# r模式:将3.txt内容读到2.txt中第二行后显示
[root@localhost ~]# sed '2r w.txt' 2.txt
fen111
222fen
hahaha
333fen333
444xxx444
555fen555fen
# w模式:将第二行的内容写入w.txt 文件,文件不存在自动创建
[root@localhost ~]# sed '2w w.txt' 2.txt
[root@localhost ~]# cat w.txt
222fen
# y模式:将第二行内容替换
[root@localhost ~]# sed '2y/fe/FE/' 2.txt
fen111
222FEn
333fen333
444xxx444
555fen555fen
案例
1、将nginx.conf中的注释行全部去掉
[root@localhost ~]# sed '/^ *#/d' /etc/nginx/nginx.conf
2、将nginx.conf中每一行之前增加注释
[root@localhost ~]# sed 's/.*/# &/g' /etc/nginx/nginx.conf
3、要求一键修改本机的ip,
192.168.15.100 ---> 192.168.15.101
172.16.1.100 ---> 172.16.1.101
sed -i 's#.100#.101#g' /etc/sysconfig/network-scripts/ifcfg-eth[01]
4、将/etc/passwd中的root修改成ROOT
sed -i 's#root#ROOT#g' /etc/passwd
linux 三剑客之 awk
awk 是一个强大的 Linux 命令,有强大的文本格式化的能力。相对于 grep 的查找,sed 的编辑,awk 在其对数据分析并生成报告时,显得尤为强大。简单来说 awk 就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk 其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能
语法格式:
awk [选项] '模式{动作}' [文件信息]
awk [参数] [处理规则] [操作对象]
参数
-F #指定文本分隔符(默认是以空格作为分隔符)
案例:
# 准备文本文件
[root@localhost ~]# cat 3.txt
test1d test2f test3y
test4d test5f test6y
test7d test7f test9y
# 以字符d为分割符
[root@localhost ~]# awk -F'd' '{print $NF}' 3.txt
test2f test3y
test5f test6y
test7f test9y
案例:打印系统所有用户的解析器
[root@localhost ~]# awk -F: '{print $NF}' /etc/passwd
# awk中的内置变量
$0 : 代表当前行
[root@localhost ~]# awk -F: '{print $0, "---"}' /etc/passwd
$n : 代表第n列
[root@localhost ~]# awk -F: '{print $1}' /etc/passwd
NF : 记录当前行的字段数
[root@localhost ~]# awk -F: '{print NF}' /etc/passwd
$NF : 代表最后一列
[root@localhost ~]# awk -F: '{print $NF}' /etc/passwd
NR : 用来记录行号
[root@localhost ~]# awk -F: '{print NR}' /etc/passwd
FS : 指定文本内容分隔符(默认是空格)# FS 的优先级要高于 -F
[root@localhost ~]# awk 'BEGIN{FS=":"}{print $NF, $1}' /etc/passwd
OFS : 指定打印分隔符(默认空格)
[root@localhost ~]# awk -F: 'BEGIN{OFS=" >>> "}{print $NF, $1}' /etc/passwd
awk 的生命周期和处理规则的执行流程
# awk的生命周期和
grep、sed 和 awk 都是读一行处理一行,直至处理完成。
① 接收一行作为输入
② 把刚刚读入进来得到文本进行分解
③ 使用处理规则处理文本
④ 输入一行,赋值给$0,直至处理完成
⑤ 把处理完成之后的所有的数据交给END{}来再次处理
# awk处理规则的执行流程
awk [参数][分隔符] '{BEGIN{开始初需要的处理}/定位/{循环}END{结束前需要的处理}}' [操作对象]
BEGIN{} #开始语句:定义变量在BEGIN块里面
// #定位:正则匹配
{} #循环语句:循环处理文本
END{} #结束语句:打印之前统一处理
awk 中的函数
# 只能用于循环语句和结束语句
print : 打印
printf : 格式化打印
参数:
%s #字符串
%d #数字
- #左对齐
+ #右对齐
15 #至少占用15字符
[root@localhost ~]# awk -F: 'BEGIN{OFS=" | "}{printf "|%+15s|%-15s|\n", $NF,$1}' /etc/passwd
| /bin/bash|root |
| /sbin/nologin|bin |
| /sbin/nologin|daemon |
| /sbin/nologin|adm |
| /sbin/nologin|lp |
| /bin/sync|sync |
| /sbin/shutdown|shutdown |
| /sbin/halt|halt |
| /sbin/nologin|mail |
| /sbin/nologin|operator |
| /sbin/nologin|games |
......
awk 中的定位和流程控制
# awk中的定位
# 1、正则表达式
# 打印含root所在行的所有内容
[root@localhost ~]# awk -F: '/root/{print $0}' /etc/passwd
# 打印以root开头所在行的所有内容
[root@localhost ~]# awk -F: '/^root/{print $0}' /etc/passwd
# 2、比较表达式
> #大于
< #小于
>= #大于等于
<= #小于等于
~ #表示匹配后面的正则表达式
!~ #表示匹配后面的正则表达式
案例:要求打印属组ID大于属主ID的行
[root@localhost ~]# awk -F: '$4 > $3{print $0}' /etc/passwd
案例:结尾包含bash
[root@localhost ~]# awk -F: '$NF ~ /bash/{print $0}' /etc/passwd
案例:结尾不包含bash
[root@localhost ~]# awk -F: '$NF !~ /bash/{print $0}' /etc/passwd
# 3、逻辑表达式
&& #与
[root@localhost ~]# awk -F: '$3 + $4 > 2000 && $3 * $4 > 2000{print $0}' /etc/passwd
|| #或
[root@localhost ~]# awk -F: '$3 + $4 > 2000 || $3 * $4 > 2000{print $0}' /etc/passwd
! #非
[root@localhost ~]# awk -F: '!($3 + $4 > 2000){print $0}' /etc/passwd
# 4、算术表达式
+ #加
- #减
* #乘
/ #除
% #取余
案例:要求属组 + 属主的ID 大于 2000
[root@localhost ~]# awk -F: '$3 + $4 > 2000{print $0}' /etc/passwd
案例:要求属组 * 属主的ID 大于 2000
[root@localhost ~]# awk -F: '$3 * $4 > 2000{print $0}' /etc/passwd
案例:要求打印偶数行
[root@localhost ~]# awk -F: 'NR % 2 == 0{print $0}' /etc/passwd
案例:要求打印奇数行
[root@localhost ~]# awk -F: 'NR % 2 == 1{print $0}' /etc/passwd
# 5、条件表达式
== #等于
> #大于
< #小于
>= #大于等于
<= #小于等于
案例:要求打印第三行
[root@localhost ~]# awk -F: 'NR == 3{print $0}' /etc/passwd
# 6、范围表达式:一个选定条件到另一个选定条件之间的数据
案例:
[root@localhost ~]# awk -F: '/^root/,/^ftp/{print $0}' /etc/passwd
# 流程控制
只存在循环之中。
if
[root@localhost ~]# awk -F: '{if($3>$4){print "大于"}else{print "小于或等于"}}' /etc/passwd
if(){}
if(){}else{}
if(){}else if(){}else{}
for
[root@localhost ~]# awk -F: '{for(i=10;i>0;i--){print $0}}' /etc/passwd
for(i="初始值";条件判断;游标){}
while
[root@localhost ~]# awk -F: '{i=1; while(i<10){print $0, i++}}' /etc/passwd
while(条件判断){}
每隔5行,打印一行横线
-------------------------------------------------------------------------
[root@localhost ~]# awk -F: '{if(NR%5==0){print "----------------"}print $0}' /etc/passwd