前几篇梳理了 sed 命令,grep命令,awk命令的用法和常用的使用场景,感受到了文本三剑客的强大和实用,接下来,根据实际用到的例子,演示下他们的组合使用场景。
示例1:批量替换多个文件中的指定内容
以上篇的 a1.txt 、b1.txt 两个文件(当前目录下就这两个文件)为例,将当前目录下所有文件中 "utf" 字符串替换成 "GBK"。思路无疑是先查找再编辑,也就是说,可以使用 grep 查找再 sed 编辑,或者 grep + awk 组合查找,再 sed 编辑,这里总结了2种可行的方式。
方式一:
sed -i "s/原字符串/新的字符串/g" `grep -i 原字符串 -rl 所在目录`
测试一下,为方便测试看效果,sed 命令暂不使用 -i 参数:
$ sed 's/utf/GBK/g' `grep -i utf -rl .`
20220630075945|yyds
20220630074530|omg
20220630073754|hot hot
20220630173530|come on
20220630075242|nice
20220630080933|today
20220630162211|yesyes
20220630162900|yeah
20220630163643|yeah yeah
20220630164303|nonono
20220629110635|cctv
20220630165049|996icu
20220630165835|enenenen
20220630170545|eeeeeeee
20220630171300|fuck trump
20220630174921|GBK
20220630172112|GBK-8
20220630074530|omg
20220630173530|come on
20220629110635|cctv13
20220630171300|fuck trump
20220630172112|GBK-8
方式二:
grep -i "原字符串" -r 所在目录 |awk -F ":" '{print $1}' |sort |uniq |xargs sed -i 's/原字符串/新的字符串/g'
简单解释下:grep 命令从所在目录,进行递归查找存在"原字符串"的文件,且忽略"原字符串"大小写,查找的结果是这个格式"xxx文件:对应的行数据",而我们需要的是"xxx文件",因此,使用 awk 命令进行提取第1列的文件名,并通过 sort 命令和 uniq 命令实现排序和去重。
xargs 是什么东西?这里重点说明下,从字面看 x 是乘号,args 是参数(arguments)的简写,放在一起就是产生某个命令的参数的含义。也就是说,xargs 可读入 stdin 数据,默认以空格符和换行符去识别,将前一个命令的 stdin 数据分隔为参数。因此,上面经排序和去重的结果(即一个个文件名并以换行符分隔)就会被分隔为多个参数,提供给后面的 sed 命令继续使用。
测试一下,为方便测试看效果,sed 命令也暂不使用 -i 参数:
$ grep -i "utf" -r . |awk -F ":" '{print $1}' |sort |uniq |xargs sed 's/utf/GBK/g'
20220630075945|yyds
20220630074530|omg
20220630073754|hot hot
20220630173530|come on
20220630075242|nice
20220630080933|today
20220630162211|yesyes
20220630162900|yeah
20220630163643|yeah yeah
20220630164303|nonono
20220629110635|cctv
20220630165049|996icu
20220630165835|enenenen
20220630170545|eeeeeeee
20220630171300|fuck trump
20220630174921|GBK
20220630172112|GBK-8
20220630074530|omg
20220630173530|come on
20220629110635|cctv13
20220630171300|fuck trump
20220630172112|GBK-8
示例2:显示系统 ip
以我的虚拟机上 CentOS7.9 为例,先使用 ifconfig 命令查看网口的配置信息:
# 网口的有些信息不便于展示,使用--线替换了
$ ifconfig
ens33: flags=xxx<UP,BROADCAST,MULTICAST> mtu ----
inet 192.168.150.130 netmask 255.255.255.0 broadcast 192.168.150.255
inet6 -------------------------------- prefixlen 64 scopeid ----<link>
ether ----------------- txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=xx<UP,LOOPBACK,RUNNING> mtu -----
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x--<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 496 bytes 43280 (42.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 496 bytes 43280 (42.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
virbr0: flags=xxxx<UP,BROADCAST,MULTICAST> mtu -----
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether ----------------- txqueuelen ---- (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
可以看到,我们需要的是 ens33 的第二行信息:
$ ifconfig ens33 |grep "inet "
inet 192.168.150.130 netmask 255.255.255.0 broadcast 192.168.150.255
这里,直接使用 awk 命令提取对应列并不容易,第一感觉是空格符比较多,而且也不确定空白的地方是不是空格符呢,因此需要一个个尝试:
$ ifconfig ens33 |grep "inet " |awk -F " " '{print $1}'
inet
$ ifconfig ens33 |grep "inet " |awk -F " " '{print $2}'
192.168.150.130
$ ifconfig ens33 |grep "inet " |awk -F " " '{print $3}'
netmask
效果还不错,使用第二个命令已经达到了预期的目的,系统的 ip 显示了出来。
当然,还有其他的方式可用,比如将 ip 的前后字符串清除,那肯定只会显示 ip 了,如下:
$ ifconfig ens33 |grep "inet " |sed 's/^.*inet //g' |sed 's/ *netmask.*$//g'
192.168.150.130
示例3:指定应用的进程关闭
我们知道,杀掉一个应用的进程,需要先通过 jps 命令看下进程号,然后使用 kill -9 进程号 才能关闭掉指定应用的进程。
而实际情况中,服务器上的应用通常是采用集群方式多节点部署的。当发布补丁时,每台机器都是需要重启的,使用刚才说的这种先查后杀方式进行关闭当前进程,效率极低。因此,经常使用 shell 脚本进行操作,比如编写 stop.sh,参考如下:
#!/bin/bash
kill -9 `jps |grep '你的应用名称' |awk '{print $1}'`
最后
其实,对于 sed 命令,grep 命令和 awk 命令的组合使用场景远不止这些啦,这里只是抛砖引玉。只要掌握了它们各自的特点,并能根据特定需求进行组合使用,将会极大的提高工作效率,并减少一些多余的没必要的操作。