sed编辑器
前言
在编写脚本文件时,经常需要对文件进行操作,然而vim编辑器却是一款交互式的编辑器,我们无法将其应用到脚本上,而echo配置定向符只能新增文件内容,不能进行修改的操作,这时候sed编辑器配合正则表达式就显得尤为重要了,本篇主要介绍sed编辑器的工作原理以及sed编辑器的常用选项和正则表达式的结合的应用实例,通过大量的命令实例来对sed进行深入的了解。
当然,还是欢迎各路大神前来批评指教的。
sed编辑器概念
流编辑器,是一种非交互式的编辑器,常用于脚本书写。
sed编辑器可以根据输入的命令行的命令或存储在文件中的命令处理数据。它,每次从输入读取一行数据,将该数据所提供的编辑器命令匹配,根据命令修改数据流中的数据,然后将新数据输出到STDOUT。在流编辑器将全部命令和一行数据匹配完之后,它读取下一行数据,并重复上述过程。处理完数据流中的全部数据行之后,该编辑器停止。
sed常用选项
- -n:屏蔽默认输出(全部文本)
- -i:直接修改文件内容
- -f:使用sed脚本
- -e:可指定多个处理动作
- -r:启用扩展的正则表达式,若与其他选项一起使用,应作为首个选项
- {}:可组合多个命令,以分号分隔
这里需要说明的一点有:这里的命令选项和下面的操作命令,两者是相互独立,是不同的两个部分,由于出现了相同字母的部分,所以请注意,别混淆了。
sed基本操作
下面的每一个命令都可以配合正则表达式进行操作,
在sed中使用正则表达式的格式是:‘/正则表达式/命令 [参数]’
这里使用单引号或者双引号都可以,只需要注意单引号屏蔽元字符的效果即可。
‘/IPADDR/p’
‘/line/c change line’
’s/>.*</>test</‘
打印命令
由于sed 默认会有输出,所以当使用-p选项时,会出现每一行的信息会出现两次
可以添加 -n 选项可以禁止其他所有行,即屏蔽默认输出
[root@service99 Sed]# sed 'p' sed_test.txt
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
[root@service99 Sed]# sed -n 'p' sed_test.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
其他使用实例
[root@service99 Sed]# cat test.info
DEVICE=eth1
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=none
IPV6INIT=no
USERCTL=no
IPADDR=192.168.2.99
PREFIX=24
[root@service99 Sed]# sed -n '1p' test.info //查看第一行内容
DEVICE=eth1
[root@service99 Sed]# sed -n '1,3p' test.info //查看第一,三行内容
DEVICE=eth1
TYPE=Ethernet
ONBOOT=yes
[root@service99 Sed]# sed -n '1~2p' test.info //查看从第一行开始步数为2的查看文件内容,此例情况即是1,3行内容
DEVICE=eth1
ONBOOT=yes
IPV6INIT=no
IPADDR=192.168.2.99
对于sed中行号的说明,其实就是单纯的使用“1”“1,3”“1~3”,上述的命令只是列举,其他指令也是类似的。
在这一部分我们还可以与正则表达式进行配合使用,‘/正则表达式/p’。
[root@service99 Sed]# sed -n '/root/p' sed_test.txt
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@service99 Sed]# sed -n '/ro*/p' sed_test.txt
root:x:0:0:root:/root:/bin/bash
adm:x:3:4:adm:/var/adm:/sbin/nologin
sed文本替换
在进行文本替换这一块,可以说sed1的这一功能也许是用的最多的,因为很多配置文件的修改都需要进行该功能,然而想要替换文本,那么首先我们需要找到的要修改的文本,说到了查找那就需要使用正则表达式了。
sed文本替换命令格式:
sed ‘s/原内容/替换内容/[替换标记]’
在原内容的地方,我们可以使用正则表达式来匹配文本,也可以直接输入替换文本。
不过由于正则表达式中就有一个单词匹配的功能,那么也就可以直接理解为就是使用正则表达式。
[root@service99 Sed]# cat replace_test.txt
2016 2017 2016 end1
asdf 2016 test bind
name 2016 2016 tftp
2016 2016 2016 2016
2017 2017 0000 ssss
[root@service99 Sed]# sed 's/2016/2017/' replace_test.txt
2017 2017 2016 end1
asdf 2017 test bind
name 2017 2016 tftp
2017 2016 2016 2016
2017 2017 0000 ssss
[root@service99 Sed]# sed 's/end1/END1/' replace_test.txt
2016 2017 2016 END1
asdf 2016 test bind
name 2016 2016 tftp
2016 2016 2016 2016
2017 2017 0000 ssss
替换命令在替换多个文本行中的文本效果不错,但是你们也发现了,
默认情况下仅替换各行中首次出现的文本。
要是替换命令继续替换之后的文本,则必须使用替换标记(substitution flag),替换标记要放在替换命令字符串之后:
s/pattern/replacement/flags
可用的替换标记有四种:
- 数字:表示新文本的替换模式
- g:表示用新文件替换现有文本的全部实例
- p:表示打印原始行数据
- w file:将替换的结果写入文件
[root@service99 Sed]# cat replace_test.txt
2016 2017 2016 end1
asdf 2016 test bind
name 2016 2016 tftp
2016 2016 2016 2016
2017 2017 0000 ssss
[root@service99 Sed]# sed 's/2016/XXXX/2' replace_test.txt //替换每行第二个2016为XXXX
2016 2017 XXXX end1
asdf 2016 test bind
name 2016 XXXX tftp
2016 XXXX 2016 2016
2017 2017 0000 ssss
[root@service99 Sed]# sed 's/2016/XXXX/' replace_test.txt //替换每行第一个2016为XXXX
XXXX 2017 2016 end1
asdf XXXX test bind
name XXXX 2016 tftp
XXXX 2016 2016 2016
2017 2017 0000 ssss
[root@service99 Sed]# sed 's/2016/XXXX/1' replace_test.txt //替换每行第一个2016为XXXX
XXXX 2017 2016 end1
asdf XXXX test bind
name XXXX 2016 tftp
XXXX 2016 2016 2016
2017 2017 0000 ssss
[root@service99 Sed]# sed 's/2016/####/g' replace_test.txt //替换全部2016为####
#### 2017 #### end1
asdf #### test bind
name #### #### tftp
#### #### #### ####
2017 2017 0000 ssss
[root@service99 Sed]# sed -n 's/2016/2019/p' replace_test.txt //将替换结果打印输出(没有进行操作的行就不会打印出来,当文件内容较多时都需要添加该标记)
2019 2017 2016 end1
asdf 2019 test bind
name 2019 2016 tftp
2019 2016 2016 2016
//标记的同时使用
[root@service99 Sed]# sed 's/2016/####/2g' replace_test.txt 替换每行第二个(包括第二个)之后所有的2016为####
2016 2017 #### end1
asdf 2016 test bind
name 2016 #### tftp
2016 #### #### ####
2017 2017 0000 ssss
[root@service99 Sed]# sed -n 's/2016/####/2gp' replace_test.txt
2016 2017 #### end1
name 2016 #### tftp
2016 #### #### ####
替换字符
有时可能会在文本字符串中遇到不容易在替换模式中使用的字符,在Linux中最常见的一个例子是正斜杠。
//将 /bin/bash 替换为 /sbin/nologin
[root@service99 Sed]# cat sed_test.txt | head -5
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@service99 Sed]# sed -n 's//bin/bash///sbin/nologin/p' sed_test.txt
sed: -e expression #1, char 8: unknown option to `s' //直接修改会报错,由于此时“/”有特殊含义
//需要使用转义字符 “\”
// 配合 -n ‘p’ 可以更清晰的看见修改效果
[root@service99 Sed]# sed -n 's/\/bin\/bash/\/sbin\/nologin/p' sed_test.txt
root:x:0:0:root:/root:/sbin/nologin
大家可能觉得这样书写起来十分麻烦,容易混淆和出现错误。
sed允许为替换命令中的字符定界符选择一个不同的字符,这个不同的字符可以是任意字符。
[root@service99 Sed]# sed -n 's#/bin/bash#/sbin/nologin#p' sed_test.txt
root:x:0:0:root:/root:/sbin/nologin
sed 's9\98\998\97\99' temp_passwd.info //将989替换为8979
sed替换的特殊用法:
sed 's/abc/&d' file //将abc替换为abcd,“/abc/”复制,“&”表示粘贴。该功能可以使用扩展正则的保留“()”“\1”实现
替换命令和正则表达式的配置使用,这里我们以一个实例来进行了解:
在Linux上使用KVM虚拟机时,我们经常有克隆(clone)的需求,在克隆新虚拟机时,需要进行的操作有:
- 系统镜像文件(img)的增量/快照备份或完全clone
- 虚拟机配置文件的修改
Linux中需要安装虚拟化组包。
可以使用命令:yum grouplist "Virtual*"
查找
查找结果(英文环境,中文环境下显示的会是汉字)
Virtualization
Virtualization Client
Virtualization Tools
Virtualization Platform
然后使用命令yum groupinstall "组包名“
进行安装。
安装好之后,就可以
ls /etc/libvirt/qemu/
networks rh6_node01.xml rh6_node02.xml rh6_node03.xml rh6_node08.xml rh6_node09.xml rh6_node97.xml rh6_node99.xml
这里我们使用sed来修改虚拟机的配置文件(.xml):
[root@service99 Sed]# cat /etc/libvirt/qemu/rh6_node97.xml
[root@service99 Sed]# cp /etc/libvirt/qemu/rh6_node97.xml kvm_virtual.xml
//需要修改的参数有:name,uuid,source file.mac
[root@service99 Sed]# sed -n '/<name>/p' kvm_virtual.xml
<name>rh6_node97</name>
[root@service99 Sed]# sed 's/<name>.*<\/name>/<name>clone_title<\/name>/' kvm_virtual.xml
//两条命令等价
//[root@service99 Sed]# sed 's#<name>.*</name>#<name>clone_title</name>#' kvm_virtual.xml
//还有其他的方法
[root@service99 Sed]# sed -n '/<name>/ s#>.*<#>clone_title<#p' kvm_virtual.xml
<name>clone_title</name>
[root@service99 Sed]# sed -i '/<name>/ s#>.*<#>clone_name<#' kvm_virtual.xml
/*前面几条没有添加-i选项的修改,都只是将修改结果输出到了屏幕上,并没有对原文进行修改
请一定要注意,不要使用-ni命令,这样会让整个文件就剩下你刚才替换的那一行的
即 不要使用这条命令:sed -ni '/<name>/ s#>.*<#>clone_name<#p' kvm_virtual.xml
-n的含义是禁止其他的所有行,-i将结果写入文件
*/
[root@service99 Sed]# sed -n "/uuid/ s/>.*</>`uuidgen`</p" kvm_virtual.xml
<uuid>c93d3b1e-45b3-4565-a297-9a14dc5f3308</uuid>
//[root@service99 Sed]# sed -n "/uuid/ s/>.*</>$(uuidgen)</p" kvm_virtual.xml
<uuid>95b8da29-3204-4432-b81e-5fd135c31c9e</uuid>
//注意这个地方要是使用uuidgen这个方法,那么请不要使用单引号,这样会屏蔽函数效果
//所以请使用双引号,sed的语句中,使用单双引号都时可以的,只要注意屏蔽元字符的效果就可以了
[root@service99 Sed]# sed -i "/uuid/ s/>.*</>`$uuidgen`</" kvm_virtual.xml //文件修改
[root@service99 Sed]# sed -n "/source file/ s#='.*'#='/img/clone_tmp.img'#p" kvm_virtual.xml
<source file='/img/clone_tmp.img'/>
[root@service99 Sed]# sed -n "/source file/p" kvm_virtual.xml
<source file='/img/clone.img'/>
[root@service99 Sed]# sed -i "/source file/ s#='.*'#='/img/clone_tmp.img'#" kvm_virtual.xml
[root@service99 Sed]# sed -n "/source file/p" kvm_virtual.xml
<source file='/img/clone_tmp.img'/>
/*
现在又会有一个容易出错的地方:
错误命令:sed -i "/source file/ s#='.*'#='/img/clone_tmp.img'#p" kvm_virtual.xml
在后面的那个命令p若是和-i一起执行。就会让原始文件中会将操作的这几行都会再重复出现一边,并在源文件中响应了这些操作。
*/
sed插入和附加文本
- 插入命令(i)在指定行之前插入一行
- 附加命令(a)在指定行后面添加新的一行
[root@service99 Sed]# echo "Line one" | sed 'i FIrsrt'
FIrsrt
Line one
[root@service99 Sed]# echo "Line one" | sed 'a FIrsrt'
Line one
FIrsrt
当进系sed的一些文件修改的操作时,sed修改的并不是源文件的内容,所以需要添加-i选项,直接修改原文件。
在这李需要注意的是:-i 和 ‘i’ 并不是一回事,前者是sed的一个选项,后者是插入命令。
[root@service99 Sed]# echo "Line one" > line.txt
[root@service99 Sed]# sed 'i "Inster line"' line.txt
"Inster line"
Line one
[root@service99 Sed]# cat line.txt
Line one
[root@service99 Sed]# sed -i 'i Inster line' line.txt
[root@service99 Sed]# cat line.txt
Inster line
Line one
如果数据流有多行,而现在需要给最后一行添加数据,则可以:
[root@service99 Sed]# cat line.txt
Inster line
Line one
[root@service99 Sed]# sed '$a last line' line.txt
Inster line
Line one
last line
现在进行一个操作,个给文件第一行后面添加多行文本:
[root@service99 Sed]# cat test.info
DEVICE=eth1
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=none
IPV6INIT=no
USERCTL=no
IPADDR=192.168.2.99
PREFIX=24
[root@service99 Sed]# sed '1a \
This is add line 1.\
This is add line 2.\
This is add line 3.' test.info
//输出结果,没有添加-i选项所以并没有对原文修改
DEVICE=eth1
This is add line 1.
This is add line 2.
This is add line 3.
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=none
IPV6INIT=no
USERCTL=no
IPADDR=192.168.2.99
PREFIX=24
//还可以在第二行之前插入,即使用‘i’
更改行
更改行命令允许更改数据流中整行文本的内容。
[root@service99 Sed]# cat line.txt
This is line 1.
This is line 2.
This is line 3.
This is line 4.
[root@service99 Sed]# sed '3c This is change line 3.' line.txt
This is line 1.
This is line 2.
This is change line 3.
This is line 4.
在这里有一个特别的情况,需要说明一下:在更改命令中,可以使用地址范围,但其结果可能不是预期的效果。
[root@service99 Sed]# sed '2,3c \
> This 2,3 change line .' \
> line.txt
This is line 1.
This 2,3 change line .
This is line 4.
//sed编辑器没有用该文本更改两行,而是使用单一文本行替换了两行
改变行命令和正则表达式的联合使用
[root@service99 Sed]# cat /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=none
IPV6INIT=no
USERCTL=no
IPADDR=192.168.2.99
PREFIX=24
[root@service99 Sed]# sed '/IPADDR/c IPADDR=192.168.4.99' /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=none
IPV6INIT=no
USERCTL=no
IPADDR=192.168.4.99
PREFIX=24
变换命令
变换命令(y)是唯一对单个字符进行操作的sed编辑器命令。
命令格式:[address]y/inchars/outchars/
变换命令将inchars和outchars的值进行一对一映射。即inchars的第一个值对应outchars的第一个值,以此类推,若是inchars和outchars的长度不等,则会出现报错。
[root@service99 Sed]# cat line.txt
This is line 1.
This is line 2.
This is line 3.
This is line 4.
[root@service99 Sed]# sed 'y/123/789/' line.txt
This is line 7.
This is line 8.
This is line 9.
This is line 4.
替换命令应用示例
sed -r 's/^.(.*).$/\1/' sed_02_test.info //删除每一行内的第一个和最后一个字符
sed -r 's/^..(.*)..$/\1/' sed_02_test.info //删除每一行内前两个字符和最后两个字符
sed -r '1s/^..(.*)..$/\1/' sed_02_test.info //删除第一行的前两个字符和最后两个字符
sed -r 's/^(.).(.*).(.$)/\1\2\3/' sed_02_test.info //删除每一行内第二个字符和倒数第二个字符
sed -r 's/^(.).(.*).(.)$/\1\2\3/' sed_02_test.info //同上,测试在进行“()”复制操作时,^$是否一定要放置在()内部,测试结果,是没有影响
sed -r 's/(^.).(.*).(.$)/\1\2\3/' sed_02_test.info
//sed -r 's/(^.).(.)*.(.$)/\1\2\3/' sed_02_test.info //错误实现,该表达式只是复制其中一个字符,并不满足需求
//可以使用s将旧的东西替换为空 s/old
sed的n指令(next)读取下一行
/*
sed命令执行时,会先进行读取数据的操作,当读取到数据后,就会按照指令进行对应的操作(比如p打印,d删除),而n指令需要进行的操作是要求sed直接去读取下一行的数据,这样论转下来,就会出现‘n;p’ 可以输出偶数行,‘p:n’可以输出奇数行
即好比一个循环,n指令表示continue命令跳出循环
*/
sed -n 'n;p' sed_test.info //输出偶数行
sed -n 'n;d' sed_test.info //删除偶数行
sed -n 'p;n' sed_test.info //输入奇数行
sed -n '$=' sed_test.info //显示最后一行的行号
sed '4,7s/^/#/' a.txt //4,7行添加注释
sed 's/^#anon/anon/' vsftpd.conf
sed 's/^#anon/anon/' /etc/vsftpd/vsftpd.conf
sed -n '/^DocumentRoot/p' /etc/httpd/conf/httpd.conf
sed -i '/^DocumentRoot/ s#".*"#"/opt/wwwroot"#' /etc/httpd/conf/httpd.conf