1、概述
sed的作用我们就记得两条就可以了:替换和增删改查,也就是说今后当我们想对文件进行一些文字的替换和增删改查时就要想起sed。
sed的语法分成三部分,我们来举一个例子:
//sed <选项-n> <对谁操作,3代表第3行> <干啥p,p代表打印> <要操作的文件> sed -n 3p /etc/passwd
sed的执行过程:
将文件"吸入"内存,然后在内存里面处理,处理好之后将空间内的内容倾倒到屏幕。
sed常用的选项其实就三个最为常用:
2、查-n:仅显示处理的行
-r:使其支持扩展的正则表达式
-i:sed默认不改变文件的内容,使用-i会改变文件的内容,慎用!
-e:-e选项允许在同一行里执行多条命令(不好用)
1、打印整行(一或多)
1、显示文件的哪一行,或是哪几行,要求我们提示知道要显示的东西哪几一行,如果不知道想要的行在文件是第几行,那么可以先用cat -n或是less -N进行查看确认,然后再用sed打印。
//通过这个例子体会`-n`这个选项的作用 [root@zabbix3 tmp]# cat test.txt zhanghe zhangjia zhangwei [root@zabbix3 tmp]# sed 2p test.txt zhanghe zhangjia zhangjia zhangwei [root@zabbix3 tmp]# sed -n 2p test.txt zhangjia
[root@zabbix3 tmp]# cat test.txt zhanghe zhangjia zhangwei //仅打印最后一行 [root@zabbix3 tmp]# sed -n '2p' test.txt zhangjia //打印最后一行 [root@zabbix3 tmp]# sed -n '$p' test.txt zhangwei //下面这两个例子效果是一样的 [root@zabbix3 tmp]# sed -n '2,3p' test.txt zhangjia zhangwei [root@zabbix3 tmp]# sed -n '2,+1p' test.txt zhangjia zhangwei
2、正则打印包含关键字的行
2、我们想要显示出现某个关键字的行,比如找出/etc/passwd当中开头是zhanghe关键字的行,我们就得使用正则表达式进行匹配,在sed当中一旦想要使用正则表达式的话就要使用//这两个符号,在这两上符号内部写正则表达式。
//其实不用sed,通过grep实现这个需求更简单,不是吗?如下所示: [root@zabbix3 ~]# grep "^zhanghe" /etc/passwd zhanghe:x:1000:1000::/home/zhanghe:/bin/bash //下面是这是通过sed实现的,看着还麻烦一点 [root@zabbix3 ~]# sed -n '/^zhanghe/p' /etc/passwd zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
// 显示结尾是/bin/bash的行 [root@zabbix3 ~]# grep '/bin/bash$' /etc/passwd root:x:0:0:root:/root:/bin/bash zhanghe:x:1000:1000::/home/zhanghe:/bin/bash //下面是这是通过sed实现的 [root@zabbix3 ~]# sed -n '/\/bin\/bash$/p' /etc/passwd root:x:0:0:root:/root:/bin/bash zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
显示开头是root或者zhanghe的行
//“|”符号属于扩展的正则,所以sed加r,grep加e [root@zabbix3 ~]# egrep '^(root|zhanghe)' /etc/passwd root:x:0:0:root:/root:/bin/bash zhanghe:x:1000:1000::/home/zhanghe:/bin/bash [root@zabbix3 ~]# sed -rn '/^(root|zhanghe)/p' /etc/passwd root:x:0:0:root:/root:/bin/bash zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
打印出开头是root行一直到结尾nologin的中间的行,我们还是可以使用正则表达式
[root@localhost ~]# sed -n '/^root/,/nologin$/p' /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin
其实用正则表达式匹配范围非常适合我们用来查看日志,假如说我们查看一下某个时间点到另一个时间点之间发生了什么事情?如下所示:
[root@localhost ~]# sed -n '/Oct 21 15:42:48/,/Oct 21 16:09:26/p' /var/log/secure2、增
增常常于查连用,而查无非是根据行号和正则表达式查询,一定要体会到这个联系!才会把sed的查和增联系起来
我们在文本后面增加东西有几种方法呢?vim、nano、>>(echo 、cat)除了这些方法之后,在文本里面增加内容也只有sed了。
通过sed在文本里面增加内容实际上非常的简单,主要就用到三个选项:a/i/c。
a:apped在匹配到的行下面加
i:insert在匹配到的行上面加
r:在行的后面加内容,加的内容必须来自于文件
c:把这一行内容替换成你想要的内容 #看着是不是有点熟悉,grep也有类似的选项
y:作用字符替换,将匹配的内容做替换
w:将匹配到的行输出到另一个文件
//在文本的第3行下面添加两行内容 [root@localhost ~]# sed '3azhanghe\nzhanghe' /etc/passwd zhanghe,zhanghe //在开头是root的行下面添加两行内容zhanghe,zhanghe [root@localhost ~]# sed '/^root/azhanghe\nzhanghe' /etc/passwd //把第一行整体替换成zhanghe,这是替换的一整行 [root@localhost ~]# sed '1czhanghe' /etc/passwd | head -2 zhanghe bin:x:1:1:bin:/bin:/sbin/nologin //在/etc/passwd文件里面匹配到以root开头的行,然后这一行的下一行添加/tmp/text.txt里面的内容 sed '/^root/r /tmp/text.txt' /etc/passwd //把text.txt文件里面包括my的行放置到text2.txt里面,注意这里面的顺序 sed -n '/my/w test2.txt' test.txt 将前10行当中的所有小写的s转换成大写的S和将全文所有小写的s转成大写的S sed '1,10y/s/S/' /etc/passwd3、删
删除最简单的了,就是使用一个局部命令d就可以了,如下所示:
//删除第一行 sed '1d' /tmp/passwd //删除1、2、3行 sed '1,3d' /etc/passwd //删除开头的root的行一直到结尾是nologin的行 sed '/^root/,/nologin$/d' /tmp/passwd
//删除开头是#号的行 sed '/^#/d' /etc/nginx/nginx.conf //删除真空行 sed '/^$/d' /etc/nginx/nginx.conf //删除带空格的假空行(平时记住这个即可) sed '/^[[:space:]]*$/d' /etc/nginx/nginx.conf4、替换
在sed的替换功能这里面我们要对替换做一个总结,文本的替换有很多方法,我们来总结一下,当前讲的sed的通用替换方式即:s@@@这种方式, 这种方式是通用的查找到什么就能替换什么,灵活强大,某字符的替换、大小写的替换皆可做,但是整行的替换通过s@@@不太好做,需要用到二级命令c
//字符替换(词语、单个字符) sed 's@root@R00T@g' /etc/passwd sed '1,10y/s/S/' /etc/passwd tr 's' 'S' < /etc/passwd //大小写的转换,u和l代表和upper和lower sed 's@[a-z]@\u&@g' file sed 's@[A-Z]@\l&@g' file tr '[a-z]' '[A-Z]' < /etc/passwd tr '[[:lower:]]' '[[:upper:]]' < /etc/passwd //整行替换,将第二行无论什么内容都替换成888 sed 2c888 /etc/passwd
替换是sed最重要的功能,也比较简单,我们只需要记住sed替换的标准格式,即:
//s是`sub`的意思,g是`global`就是全局的意思,整体意思就是全局替换。 sed 's@@@g'
[root@localhost tmp]# cat test.txt zhanghe zhangmin zhangjia zhanghe zhanghezhanghe // 不加g,只会替换第一行 [root@localhost tmp]# sed 's@zhanghe@hello@' test.txt hello zhangmin zhangjia hello hellozhanghe //加上g就是全局替换 [root@localhost tmp]# sed 's@zhanghe@hello@g' test.txt hello zhangmin zhangjia hello hellohello
其实g所在的位置指代的是哪几代,g是指全部嘛,如果写一个3那就是第三列,也就是说我们可以指定替换哪一列当中的字符串,如下所示,我们替换第三列当中的zhanghe为hello:
[root@localhost tmp]# cat test.txt zhanghe zhangmin zhangjia zhanghe zhanghezhanghe zhanghe //#只有第三列变化了,第一列和第二列的zhanghe都没有被替换 [root@localhost tmp]# sed 's@zhanghe@hello@3' test.txt zhanghe zhangmin zhangjia zhanghe zhanghezhanghe hello #注意,这是按照词语进行替换的,我们上面讲的-c选项是按行进行替换的。
在替换当中,只有第一个条件可以使用模式,第二个不可以
[root@zhanghe ~]# cat zh.txt i like on,my,love [root@zhanghe ~]# sed 's#\(l..e\)#\1r#' zh.txt #把l..e替换成l..er i liker on,my,lover [root@zhanghe ~]# sed 's#l\(..e\)#L\1#' zh.txt #仅把l..e的l替换成大写 i Like on,my,Love
//利用sed命令把history开始的空白字符给删除了 history | sed 's@^[[:space:]]*@@' history | sed 's@^[[:space:]]\+@@g' history|sed 's#^[[:space:]]\{3\}##g'5、后向引用
所谓的后向引用就是将想要引用的东西用括号包起来,如果再用到的话就可以直接调用了,就是这么简单。
[root@localhost tmp]# echo 123456 | sed -r 's@(.*)@\1@g' #.*就代表所有 123456 [root@localhost tmp]# echo 123456 | sed -r 's@1234(.*)@\1@g' #这个所有指代的就是5和6 56
//取IP [root@zabbix3 ~]# ifconfig eth0 | sed -n 2p inet 192.168.80.199 netmask 255.255.255.0 broadcast 192.168.80.255 [root@zabbix3 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*et (.*) net.*@\1@' 192.168.80.1996、结合
同时执行多条sed语句,-e选项允许在同一行里执行多条命令
//先将第2行到最后一行给删除了,只留下第一行,然后将第一行的ROOT替换成TTTTT sed -e '2,$d' -e 's@ROOT@TTTTT@' /etc/passwd7、练习
删除/etc/grub.conf文件中行首的空白字符(提示:替换)
[root@zhanghe ~]# sed 's@^[[:space:]]@@' /etc/grub.conf
替换/etc/inittab文件当中的id:3:initdefault:一行当中的数字为5(提示:后向引用)
[root@A ~]# sed "s%^id:[0-9]:initdefault:$%id:5:initdefault:%" /etc/inittab [root@China ~]# sed "s@\(id:\)[0-9]\(:initdefault:\)@\15\2@g" /etc/inittab sed "s@\(id:\)[[:digit:]]\(:initdefault:\)@\16\2@" /etc/inittab sed -r -i "s@(id:)[[:digit:]](:initdefault:)@\16\2@" /etc/inittab
删除/etc/inittab文件当中的空白行(提示:删除)
[root@China ~]# sed "/^[[:space:]]*$/d" /tmp/grub.conf
删除/etc/inittab文件当中以#开头的行(提示:删除)
[root@zhanghe ~]# sed "/^#/d" /etc/inittab
删除某文件中开头的#及后面的空白字符的行,但要求#号后面必须有空白字符(提示:删除)
[root@zhanghe ~]# sed "/^#[[:space:]]/d" test.sh
删除某文件中以空白字符后面跟#号的行中开头的空白字符及#(提示:删除)
[root@zhanghe ~]# sed "s@^[[:space:]]\+#@@" test.sh
取出一个文件路径的目录名称(后向引用)
[root@China ~]# echo "/etc/sysconfig" | sed 's@[^/]\+$@@' /etc/ [root@China ~]# echo "/etc/sysconfig/" | sed 's@[^/]\+$@@' /etc/sysconfig/ [root@China ~]# echo "/etc/rc.d" | sed -r "s@^(/.*/)[^/]+/?@\1@g" /etc/ [root@China ~]# echo "/etc/rc.d" | sed -r "s@[^/]+/?\$@@g" /etc/
解析:第一回是在线开头的字符至少出现一次,并且还要在词尾给删了,删除之后可不就剩下斜线开头的目录了嘛,但是,如果目录是一个绝对路径呢?就像
echo /zhang/he/ | sed "s@\(\/[[:alnum:]]\+\/\)\([[:alnum:]]\+\/\?\)@\1@" /zhang/ echo /zhang/he/ | sed "s@\(\/[[:alnum:]]\+\/\)\([[:alnum:]]\+\/\?\)@\2@" he/
取出一个目录的基名和目录名()
[root@China ~]# basename /etc/sysconfig sysconfig [root@China ~]# dirname /etc/sysconfig /etc
把/etc/fstab当中空行和开头是空格的、开头是#号都删除掉(提示:删除)
//把/etc/fstab当中空行和开头是空格的、开头是#号都删除掉 sed 's@^#@@' /etc/fstab | sed '/^[[:space:]]*$/d' | sed 's@^[[:space:]]@@' //把/etc/fstab当中空行和开头是空格的、开头是#号的行都删除掉,注意,上面是删除字,实质是替换,这里是删行 sed '/^#/d' test.txt | sed '/^[[:space:]]/d' | sed '/^[[:space:]]*$/d'
打印奇数或偶数行
sed -n 'p;n' <file> #打印奇数行 sed -n 'n;p' <file> #打印偶数行
打印完前三行后,退出sed
[root@zhanghe ~]# sed '3q' /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
将文件当中所有的字母转成大写
最好的办法不是使用sed而是使用tr,如果tr命令的话是这样:
tr 'a-z' 'A-Z' < file
将文件当中前10的abcde转成为大写
sed '1,10y/abcde/ABCDE/' /etc/passwd
将前10行当中的所有小写的s转换成大写的S和将全文所有小写的s转成大写的S
sed '1,10y/s/S/' /etc/passwd sed 's@s@S@g' /etc/passwd8、进阶
是否真正理解了sed是一个行编辑器?
sed是一个行编辑器,行编辑器的意思是只能一行行的处理,比如你可以删除任意行,但不能删除某一行当中的某一个字段,如下所示:
//删除第二行 [root@n9 ~]# cat -n /etc/issue 1 \S 2 Kernel \r on an \m 3 [root@n9 ~]# cat -n /etc/issue | sed 2d 1 \S 3 //删除第二行当中on单词,是无法删除的,无法删除的原因并不是没有匹配上,而是做不到, [root@n9 ~]# cat -n /etc/issue | sed -n 2p | sed '/on/d' 2 Kernel \r on an \m //通过grep我们可以确定最后一个sed是一定是匹配到了on,但sed删除功能的细粒度只是行而已,做不到仅删除字符串。
在使用替换s@A@B@格式的时候,第一次匹配,也就是A处的匹配一定要把一整行全都匹配上,不能仅仅匹配一行当中的某个或某些字段,这么说有些抽象,我们用例子来说明:
[root@n9 ~]# ifconfig eth0 | sed -n 2p inet 192.168.80.59 netmask 255.255.255.0 broadcast 192.168.80.255 [root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask@\1@' 192.168.80.59 255.255.255.0 broadcast 192.168.80.255 [root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask.*@\1@' 192.168.80.59
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask@\1@' 192.168.80.59 255.255.255.0 broadcast 192.168.80.255
上面这个为什么会出错呢?不是因为扩展的正则表达式写错了,那我怎么肯定我没有写错呢?通过egrep就可以确定,我把sed使用的扩展正则表达式^.*inet (.*) netmask放到egrep里面,就能看见到底匹配到了哪些内容,如下所示,内容并没有匹配错:
[root@n9 ~]# ifconfig eth0 | egrep -o '^.*inet (.*) netmask' inet 192.168.80.59 netmask
//这条命令到底错了哪里? [root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask@\1@' 192.168.80.59 255.255.255.0 broadcast 192.168.80.255
如上所示,在s@^.*inet (.*) netmask@\1@正则表达式当中,(.*)匹配的内容并不仅仅匹配的是inet后面的内容和netmask前面的内容,(.*)的真正的含义是匹配除了^.*inet匹配到的,和除了netmask这个单词之外内容,结果就是就把192.168.80.59 255.255.255.0 broadcast 192.168.80.255给匹配出来了。
解决办法我们上面说过了,只要在第一次匹配的时候把要处理的行匹配完整,就不会有这种情况发生,也就是在netmask后面加上.*,这样就把一行匹配完整了,下面举几个例子:
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask.*@\1@' 192.168.80.59
ip addr show eth0 | sed -n 3p inet 192.168.80.59/24 brd 192.168.80.255 scope global noprefixroute eth0 ip addr show eth0 | sed -n 3p | sed -r 's@[[:space:]]+inet (.*) brd@\1@' 192.168.80.59/24 192.168.80.255 scope global noprefixroute eth0 ip addr show eth0 | sed -n 3p | sed -r 's@[[:space:]]+inet (.*) brd.*@\1@' 192.168.80.59/24 ip addr show eth0 | sed -n 3p | sed -r 's@[[:space:]]+inet (.*) (\bbrd\b.*eth0$)@\1@' 192.168.80.59/24