一、sed介绍
Linux中,常使用流编辑器sed 进行文本替换工作。与交互式编辑器(例如vim)不同,sed编辑器以批处理的方式来编辑文件。
一次从输入中读取一行数据
根据所提供的编辑器命令匹配数据
按照命令修改流中的数据
将新的数据输出到 STDOUT(标准输出,即显示器)
在sed编辑器匹配完一行数据后,它会读取下一行数据并重复这个过程,直到处理完所有数据
二、sed 语法
sed [options] edit_commands [file]
# [ ]中的内容为可选可不选
Bash
注意: sed 和grep 不同,不管是否找到指定的模式,它的退出状态都是0,只有当命令存在语法错误时,sed的退出状态才是非0
常用选项
选项 功能
-n(silent) 使用安静(silent)模式。来自stdin的资料一般都会被列出到屏幕,加上-n后,则只有经过sed处理的行才会被列出
-e(expression) 允许多点编辑
-f(file) 将sed的动作写在一个文本内,-f filename 则可以执行filename内的sed动作。
-r(regexp) 支持扩展正则
-i(in place) 直接修改源文件
三、sed子命令
普通子命令 功能
a 追加
c 修改
i 插入
d 删除
w 将行写入文件
y 将字符转换为另一字符
p 打印
r 读入字符串到文件
q 退出sed
= 打印当前行号
s 替换
子命令取反
! 取反
Bash
flag标记
g 单行全局
p 打印
数字 操作到第几个匹配
Bash
高级子命令 功能
h 拷贝pattern space的内容到holding buffer
H 追加pattern space的内容到holding buffer
g 获得holding buffer中的内容,并替代当前pattern space中的文本
G 获得holding buffer中的内容,并追加到当前pattern space的后面
x 交换暂存缓冲区与模式空间的内容
n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令
N 追加新行到模式空间
P 打印模式空间中的第一行
D 删除模式空间的第一行,会导致sed从开头重新执行所有子命令
1.定址
默认sed 对文件中的所有行进行编辑。当然,也可以只指定特定的某些行,或者行范围进行流编辑,这需要用到行寻址。所指定的行地址放在子命令之前
[address]commands
Bash
数字定址
sed 编辑器将文本流中的每一行都进行编号,第一行的编号为1 ,后面的按顺序分配0行号,通过指定特定的行号,可以选择编辑特定的行
cat -n file 中 -n是为了显示行号
[root@localhost ~]# cat -n test.txt | sed '3 s/bin/BIN/g' //将第三行中所有的 bin 替换成 BIN
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sBIN:/sBIN/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
Shell
[root@localhost ~]# cat -n test.txt |sed '2,5 s/bin/BIN/g' //将第2到5行中所有的 bin 替换成 BIN
1 root:x:0:0:root:/root:/bin/bash
2 BIN:x:1:1:BIN:/BIN:/sBIN/nologin
3 daemon:x:2:2:daemon:/sBIN:/sBIN/nologin
4 adm:x:3:4:adm:/var/adm:/sBIN/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sBIN/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
Shell
$ 在正则中是匹配行尾
[root@localhost ~]# cat -n test.txt |sed '2,$ s/bin/BIN/g' //将第2行到尾行中所有的 bin 替换成 BIN
1 root:x:0:0:root:/root:/bin/bash
2 BIN:x:1:1:BIN:/BIN:/sBIN/nologin
3 daemon:x:2:2:daemon:/sBIN:/sBIN/nologin
4 adm:x:3:4:adm:/var/adm:/sBIN/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sBIN/nologin
6 sync:x:5:0:sync:/sBIN:/BIN/sync
7 shutdown:x:6:0:shutdown:/sBIN:/sBIN/shutdown
8 halt:x:7:0:halt:/sBIN:/sBIN/halt
Shell
正则定址
sed 编辑器允许使用正则过滤出命令要作用的行
/pattern/command
Bash
必须使用/ 将要指定的 pattern 包起来
sed 会寻找匹配文本模式的行,然后对这些行执行编辑命令
sed -n 中 -n是静默输出,只输出经过sed处理的行
[root@localhost ~]# sed -n '/root/s/bin/BIN/p' /etc/passwd //寻找包含有字符串 root 的行,并且将匹配行中的 bin 替换为 BIN
root:x:0:0:root:/root:/BIN/bash
operator:x:11:0:operator:/root:/sBIN/nologin
Shell
[root@localhost ~]# sed -n '/root/,/nologin/ s/bin/BIN/p' /etc/passwd //寻找包含有字符串 root 或 nologin 的行,并且将匹配行中的 bin 替换为 BIN
root:x:0:0:root:/root:/BIN/bash
BIN:x:1:1:bin:/bin:/sbin/nologin
operator:x:11:0:operator:/root:/sBIN/nologin
games:x:12:100:games:/usr/games:/sBIN/nologin
Shell
2.子命令使用
1、s 替换
使用s命令来进行文本替换操作
[root@localhost ~]# cat aaa.txt | sed 's/bin/BIN/'
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
sync:x:5:0:sync:/sBIN:/bin/sync
Shell
/字符为界定符,用于分隔字符串(sed 编辑器允许使用其他字符作为替换命令中的字符串分隔符)
使用#作为字符串分隔符
[root@localhost ~]# cat aaa.txt | sed 's#bin#BIN#'
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
sync:x:5:0:sync:/sBIN:/bin/sync
Shell
数字指明替换掉第几次匹配到的文本
没有设置这个标记时,默认是替换第一次匹配的文本
将aaa.txt中每行的第2个bin 替换为 BIN
[root@localhost ~]# cat aaa.txt | sed 's/bin/BIN/2'
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
sync:x:5:0:sync:/sbin:/BIN/sync
Shell
2、g替换所有匹配到的文本
将aaa.txt 中的 bin 全部替换为 BIN
[root@localhost ~]# cat aaa.txt | sed 's/bin/BIN/g'
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
sync:x:5:0:sync:/sBIN:/BIN/sync
Shell
3、p打印与替换命令中指定模式(srcStr)相匹配的行
使用显示行号可以看出含有bin的行被输出了两次
一次是 sed 编辑器自动输出的
另一次则是 p 标记打印出来的匹配行
[root@localhost ~]# cat -n aaa.txt | sed 's/bin/BIN/p'
1 root:x:0:0:root:/root:/BIN/bash
1 root:x:0:0:root:/root:/BIN/bash
2 BIN:x:1:1:bin:/bin:/sbin/nologin
2 BIN:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sBIN:/sbin/nologin
3 daemon:x:2:2:daemon:/sBIN:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sBIN/nologin
4 adm:x:3:4:adm:/var/adm:/sBIN/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sroot/nologin
6 sync:x:5:0:sync:/sBIN:/bin/sync
6 sync:x:5:0:sync:/sBIN:/bin/sync
Shell
将aaa.txt中所有的root 全部都替换成 ROOT,并输出被修改的行
[root@localhost ~]# cat -n aaa.txt | sed 's/root/ROOT/gp'
1 ROOT:x:0:0:ROOT:/ROOT:/bin/bash
1 ROOT:x:0:0:ROOT:/ROOT:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sROOT/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sROOT/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
Shell
如果想只输出被修改的部分可以使用sed -n静默输出
4、d删除
sed 编辑器使用 d 命令来删除文本流中的特定行。使用d命令时,一般需要带上位寻址,以删除指定的行,否则默认会删除所有文本行。
[root@localhost ~]# cat aaa.txt
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:/sroot/nologin
sync:x:5:0:sync:/sbin:/bin/sync
#使用d删除命令
[root@localhost ~]# cat aaa.txt |sed '/root/d'
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
sync:x:5:0:sync:/sbin:/bin/sync
Shell
删除第2到最后一行$
[root@localhost ~]# cat -n aaa.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sroot/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
#使用命令后
[root@localhost ~]# cat -n aaa.txt |sed '2,$d'
1 root:x:0:0:root:/root:/bin/bash
Shell
插入追加修改文本
使用i命令在sed编辑器中来向数据流中插入文本行,该命令会在指定行前增加一个新行
使用a命令在sed编辑器中来向数据流中附加文本行,该命令会在指定行后增加一个新行
注意这两个命令都不能在单行上使用(即不是用来在一行中插入或附加一段文本的),只能指定插入还是附加到另一行
sed '[address][i | a] newline' file
Shell
[root@localhost ~]# sed 'i\Insert a line behind every line' /etc/passwd # 向数据流的每一行前面增加一个新行,新行的内容为 \ 后面的内容
[root@localhost ~]# sed '1i\Insert a line behind the first line' /etc/passwd # 在数据流的第一行前面增加一个新行
[root@localhost ~]# sed '3a\Append a line after the third line' /etc/passwd # 在数据流的第三行后面增加一个新行
[root@localhost ~]# sed '$a\Append a line in the last line' /etc/passwd # 在数据流的最后一行后面增加一个新行
Shell
使用命令c可以将数据流中的整行文本修改为新的行,与插入、附加操作一样,这要求在 sed 命令中指定新的行
sed '[address][c] newtext' file
Shell
[root@localhost ~]# sed '3 c\New text' /etc/passwd # 将数据流中第三行的内容修改为 \ 后面的内容
[root@localhost ~]# sed '/root/ c\New text' /etc/passwd # 将匹配到 root 的行的内容修改为 \ 后面的内容
[root@localhost ~]# sed '2,4c\New text' /etc/passwd # 将第2到4行的内容修改为 \ 后面的内容,但是不是逐行修改,而是会将这之间的 3 行用一行文本来替代
Shell
y逐字符转换
[address]y/inchars/outchars/
Shell
转换命令会对 inchars 和 outchars 的值进行一对一的映射。inchars 中的第一个字符会被转换成 outchars 中的第一个字符;inchars 中的第二个字符会被转换成 outchars 中的第二个字符;... 直到处理完一行。如果 inchars 和 outchars 的长度不同,则 sed 编辑器会产生一个错误消息。举个例子:
[root@localhost ~]# echo abcdefggfedcba | sed 'y/acg/ACG/'
AbCdefGGfedCbA
Shell
w保存数据到文件
[address]w filename
Shell
该语句将数据流的第 1、2 行写入文件 test.txt 中去
[[email protected] ~]# sed '1,2w test.txt' /etc/passwd
[[email protected] ~]# sed -n 's/root/ROOT/g w change.txt' /etc/passwd // 将 /etc/passwd 中所有的 root 都替换成 ROOT,并将被修改的行保存到文change.txt 中去
Shell
r从文件中读取数据
filename 为要插入的文件。r 命令常结合行寻址使用,以将文本插入到指定的行后面。
可以使用 r 命令来将一个文本中的数据插入到数据流中去,与普通的插入命令 i 类似,这也是对行进行操作的,命令格式如下:
[address]r filename
Shell
将文件 test.txt 中的内容插入到数据流第三行后面去。
[root@localhost ~]# sed '3 r test.txt' /etc/passwd
Shell
四、模式空间和保持空间
模式空间和保持空间是两个独立的缓冲区,可以进行交互,命令可以寻址模式空间但是不能寻址保持空间。
模式空间:容纳当前输入行的缓冲区,通过模式匹配到的行被读入模式空间中。用来进行进一步的操作;在多行模式中,'\n'可以用来和模式空间(N命令的结果)的任意换行符匹配,单模式空间底部的换行符除外。^匹配多行的首,$匹配多行的尾,不是每行的行首和行尾
保持空间:sed在处理文本的时候都是在模式空间中进行,但有时候有些复杂的操作单一的模式空间可能无法满足需求,于是就有了保持空间,这个空间通常是空闲的,并不处理数据,只在有需要的时候和模式空间进行一些必要的数据交换。
下面是模式空间中的常用命令。
h: 把模式空间中的内容覆盖至保持空间中
H:把模式空间中的内容追加至保持空间中
g: 从保持空间取出数据覆盖至模式空间
G:从保持空间取出内容追加至模式空间
x: 把模式空间中的内容与保持空间中的内容进行互换
多行模式空间
sed命令都是一行一行的进行处理文本的,不过有些时候单行处理可能并不能满足我们的需要,所以sed还提供了多行模式,多行模式的命令主要有 NPD 三个
N:读取匹配到的行的下一行追加至模式空间
P:打印模式空间开端至\n内容,并追加到默认输出之前
D:如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本, 并不会读取新的输入行,而使用合成的模式空间重新启动循环。如果模式空间 不包含换行符,则会像发出d命令那样启动正常的新循环
1、示例
[[email protected] ~]# cat /etc/passwd |sed -n '2{N;p}'
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 读取第二行的下一行,然后输出模式空间中的内容,此时模式空间中有两行
[[email protected] ~]# cat /etc/passwd |sed -n '2{N;N;N;p}'
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
# 使用多个N命令可以读取多行进模式空间
Shell
[[email protected] ~]# seq 1 6| sed -n '1,2H;4p;5{x;p}'
4
1
2
Shell
-n 是不显示默认输出内容,1,2H是将前两行追加至保持空间,4p显示第四行,5{x;p}是在第五行的时候交换保持空间和模式空间中的内容并且输出。注意输出中的空行,这是因为H命令追加的时候是添加换行符,由于保持空间默认是空的,所以添加换行符之后就多了一个空行。以用下面的命令先往保持空间覆盖一行然后追加。
[[email protected] ~]# seq 1 6| sed -n '1h;2H;4p;5{x;p}'
4
1
2
Shell
第一个循环结束之后:模式空间为空,保持空间为第一行内容
第二个循环,将第二行追加到模式空间,此时模式空间为两行内容
第三个循环,没有匹配内容,不执行操作,模式空间和保持空间内容不变
第四个循环,读取第四行并输出,保持空间内容不变
第五个循环,读入第五行,然后和保持空间中的内容交换,之后输出。
# 暂存和取用命令:h H g G
[[email protected] ~]# sed -r '1h;$G' /etc/hosts
[[email protected] ~]# sed -r '1{h;d};$G' /etc/hosts
[[email protected] ~]# sed -r '1h; 2,$g' /etc/hosts
[[email protected] ~]# sed -r '1h; 2,3H; $G' /etc/hosts
# 暂存空间和模式空间互换命令:x
[[email protected] ~]# sed -r '4h; 5x; 6G' /etc/hosts
Shell
对于模式空间和保持空间的个人见解
[root@localhost ~]# seq 1 6 |sed -n '1H;2p;3,5{x;p}'
2
1
3
4
Shell
首先,seq 1 6 是遍历1-6,sed -n是静默输出
1H是将1放入保持空间等待
2p是将2打印出来
3,5{x;p}是3到5依次去执行{x;p}
首先是3去交换已经在保持空间内的1,然后再打印,结果就是打印1
然后是4去交换刚被交换进去保持空间的3,然后打印,结果就是打印3
再然后5去交换在保持空间的4,继续打印,结果就是打印4
最后因为是静默输出所以结果就如上面,不执行sed的就不打印显示