shell 流编辑器 sed

发布时间:2023年12月28日


一、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 中去

[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 中去
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、示例

[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命令可以读取多行进模式空间
Shell

[root@qfedu.com ~]# seq 1 6| sed -n '1,2H;4p;5{x;p}'
4

1
2
Shell
-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
Shell
第一个循环结束之后:模式空间为空,保持空间为第一行内容
第二个循环,将第二行追加到模式空间,此时模式空间为两行内容
第三个循环,没有匹配内容,不执行操作,模式空间和保持空间内容不变
第四个循环,读取第四行并输出,保持空间内容不变
第五个循环,读入第五行,然后和保持空间中的内容交换,之后输出。

# 暂存和取用命令: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
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的就不打印显示
?

文章来源:https://blog.csdn.net/weixin_52930030/article/details/135271185
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。