Shell 流编辑器 sed
shell 流编辑器 sed
一、sed介绍
Linux中,常使用流编辑器sed 进行文本替换工作。与交互式编辑器(例如vim)不同,sed编辑器以批处理的方式来编辑文件。
- 一次从输入中读取一行数据
- 根据所提供的编辑器命令匹配数据
- 按照命令修改流中的数据
- 将新的数据输出到 STDOUT(标准输出,即显示器)
在sed编辑器匹配完一行数据后,它会读取下一行数据并重复这个过程,直到处理完所有数据
二、sed 语法
sed [options] edit_commands [file]
# [ ]中的内容为可选可不选
- 注意:
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 | 替换 |
子命令取反
! 取反
flag标记
g 单行全局
p 打印
数字 操作到第几个匹配
高级子命令 | 功能 |
---|---|
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
数字定址
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
[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
$
在正则中是匹配行尾
[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
正则定址
sed 编辑器允许使用正则过滤出命令要作用的行
/pattern/command
必须使用/
将要指定的 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
[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
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
/
字符为界定符,用于分隔字符串(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
数字指明替换掉第几次匹配到的文本
没有设置这个标记时,默认是替换第一次匹配的文本
-
将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
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
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
-
将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
- 如果想只输出被修改的部分可以使用
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
-
删除第
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
插入追加修改文本
-
使用
i
命令在sed编辑器中来向数据流中插入
文本行,该命令会在指定行前增加
一个新行 -
使用
a
命令在sed编辑器中来向数据流中附加
文本行,该命令会在指定行后增加
一个新行 -
注意这两个命令都不能在单行上使用(即不是用来在一行中插入或附加一段文本的),只能指定插入还是附加到另一行
sed '[address][i | a] newline' file
[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 # 在数据流的最后一行后面增加一个新行
-
使用命令
c
可以将数据流中的整行文本修改为新的行,与插入、附加操作一样,这要求在 sed 命令中指定新的行sed '[address][c] newtext' file
[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 行用一行文本来替代
y
逐字符转换
[address]y/inchars/outchars/
转换命令会对 inchars 和 outchars 的值进行一对一的映射。inchars 中的第一个字符会被转换成 outchars 中的第一个字符;inchars 中的第二个字符会被转换成 outchars 中的第二个字符;... 直到处理完一行。如果 inchars 和 outchars 的长度不同,则 sed 编辑器会产生一个错误消息。举个例子:
[root@localhost ~]# echo abcdefggfedcba | sed 'y/acg/ACG/'
AbCdefGGfedCbA
w
保存数据到文件
[address]w filename
- 该语句将数据流的第 1、2 行写入文件 test.txt 中去
[root@qfedu.com ~]# sed '1,2w test.txt' /etc/passwd
[root@qfedu.com ~]# sed -n 's/root/ROOT/g w change.txt' /etc/passwd // 将 /etc/passwd 中所有的 root 都替换成 ROOT,并将被修改的行保存到文change.txt 中去
r
从文件中读取数据
-
filename 为要插入的文件。r 命令常结合行寻址使用,以将文本插入到指定的行后面。
-
可以使用 r 命令来将一个文本中的数据插入到数据流中去,与普通的插入命令 i 类似,这也是对行进行操作的,命令格式如下:
[address]r filename
-
将文件 test.txt 中的内容插入到数据流第三行后面去。
[root@localhost ~]# sed '3 r test.txt' /etc/passwd
-
四、模式空间和保持空间
模式空间和保持空间是两个独立的缓冲区,可以进行交互,命令可以寻址模式空间但是不能寻址保持空间。
- 模式空间:容纳当前输入行的缓冲区,通过模式匹配到的行被读入模式空间中。用来进行进一步的操作;在多行模式中,'\n'可以用来和模式空间(N命令的结果)的任意换行符匹配,单模式空间底部的换行符除外。^匹配多行的首,$匹配多行的尾,不是每行的行首和行尾
- 保持空间:sed在处理文本的时候都是在模式空间中进行,但有时候有些复杂的操作单一的模式空间可能无法满足需求,于是就有了保持空间,这个空间通常是空闲的,并不处理数据,只在有需要的时候和模式空间进行一些必要的数据交换。
下面是模式空间中的常用命令。
- h: 把模式空间中的内容覆盖至保持空间中
- H:把模式空间中的内容追加至保持空间中
- g: 从保持空间取出数据覆盖至模式空间
- G:从保持空间取出内容追加至模式空间
- x: 把模式空间中的内容与保持空间中的内容进行互换
多行模式空间
sed命令都是一行一行的进行处理文本的,不过有些时候单行处理可能并不能满足我们的需要,所以sed还提供了多行模式,多行模式的命令主要有 NPD 三个
-
N:读取匹配到的行的下一行追加至模式空间
-
P:打印模式空间开端至\n内容,并追加到默认输出之前
-
D:如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本, 并不会读取新的输入行,而使用合成的模式空间重新启动循环。如果模式空间 不包含换行符,则会像发出d命令那样启动正常的新循环
1、示例
[root@qfedu.com ~]# cat /etc/passwd |sed -n '2{N;p}'
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 读取第二行的下一行,然后输出模式空间中的内容,此时模式空间中有两行
[root@qfedu.com ~]# 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命令可以读取多行进模式空间
[root@qfedu.com ~]# seq 1 6| sed -n '1,2H;4p;5{x;p}'
4
1
2
-n 是不显示默认输出内容,1,2H是将前两行追加至保持空间,4p显示第四行,5{x;p}是在第五行的时候交换保持空间和模式空间中的内容并且输出。注意输出中的空行,这是因为H命令追加的时候是添加换行符,由于保持空间默认是空的,所以添加换行符之后就多了一个空行。以用下面的命令先往保持空间覆盖一行然后追加。
[root@qfedu.com ~]# seq 1 6| sed -n '1h;2H;4p;5{x;p}'
4
1
2
- 第一个循环结束之后:模式空间为空,保持空间为第一行内容
- 第二个循环,将第二行追加到模式空间,此时模式空间为两行内容
- 第三个循环,没有匹配内容,不执行操作,模式空间和保持空间内容不变
- 第四个循环,读取第四行并输出,保持空间内容不变
- 第五个循环,读入第五行,然后和保持空间中的内容交换,之后输出。
# 暂存和取用命令:h H g G
[root@qfedu.com ~]# sed -r '1h;$G' /etc/hosts
[root@qfedu.com ~]# sed -r '1{h;d};$G' /etc/hosts
[root@qfedu.com ~]# sed -r '1h; 2,$g' /etc/hosts
[root@qfedu.com ~]# sed -r '1h; 2,3H; $G' /etc/hosts
# 暂存空间和模式空间互换命令:x
[root@qfedu.com ~]# sed -r '4h; 5x; 6G' /etc/hosts
对于模式空间和保持空间的个人见解
[root@localhost ~]# seq 1 6 |sed -n '1H;2p;3,5{x;p}'
2
1
3
4
- 首先,
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的就不打印显示
- 首先是