这篇文章上次修改于 604 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

Sed

sed简介

  • sed是一款流编辑工具,用来对文本进行过滤和替换操作,可以对几十个文件进行统一修改
  • 原理:通过文件管道读取文件内容,默认不会修改源文件,而是将读入的内容复制到缓冲区中,成为模式空间,所有指令操作都在模式空间中进行,然后sed根据相应指令对模式空间中的内容进行处理并输出结果,默认输出至屏幕上

Sed基本语法格式

选 项含 义
--version显示sed版本
--help显示帮助文档
-n,--quiet,--silent静默输出
-e script允许多个指令脚本被执行
-f script-file从文件中读取脚本指令
-i,--in-place改选项将直接修改源文件(慎用)
-l N制定l指令可以输出行的长度
--posix禁用GUN sed扩展功能
-r在脚本指令中使用扩展正则表达式
-s,--separate默认情况下,sed把输入的多个文件名作为一个长的连续的输入流。而GUN sed则允许把它们当作单独的文件
-u,--unbuffered最低限度的缓存输入与输出

Sed入门范例

基本格式范例

  • a,append表示指令追加
  • i,insert表示插入指令
  • d,delete表示删除指令
  • s,substitution表示替换指令
[[email protected] ~]# cat test
DEVICE=eno16777736 
BOOTPROTO=static 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
 
GATEWAY=192.168.0.254 
 
ONBOOT=yes 
[[email protected] ~]# sed '2a TYPE=Ethernet' test  #在第二行之后追加TYPE=Ethernet
DEVICE=eno16777736 
BOOTPROTO=static 
TYPE=Ethernet
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
 
GATEWAY=192.168.0.254 
 
ONBOOT=yes
[[email protected] ~]# sed '2i TYPE=Ethernet' test  #在第三行之前追加TYPE=Ethernet
DEVICE=eno16777736 
TYPE=Ethernet
BOOTPROTO=static 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
 
GATEWAY=192.168.0.254 
 
ONBOOT=yes
[[email protected] ~]# sed 's/yes/no/g' test  #把文本中所有的yes替换成no
DEVICE=eno16777736 
BOOTPROTO=static 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
 
GATEWAY=192.168.0.254 
 
ONBOOT=no
[[email protected] ~]# sed '3,4d' test  #删除三四行的内容
DEVICE=eno16777736 
BOOTPROTO=static 
 
GATEWAY=192.168.0.254 
 
ONBOOT=yes
  • 如果出现不确定的行号,更多可以使用正则表达式确定操作对象
[[email protected] ~]# sed '/ONBOOT/a TYPE=Ethernet' test  #匹配包含ONBOOT的行,之后添加TYPE=Ethernet
DEVICE=eno16777736 
BOOTPROTO=static 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
 
GATEWAY=192.168.0.254 
 
ONBOOT=yes 
TYPE=Ethernet
[[email protected] ~]# sed '/^GATEWAY/d' test  #删除以GATEWAY开头的行
DEVICE=eno16777736 
BOOTPROTO=static 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
 
 
ONBOOT=yes
  • 可以将sed指令写入脚本文件中,通过sed -f来选取
[[email protected] ~]# cat sed.sh 
#This is sed.sh
/^$/d
[[email protected] ~]# sed -f sed.sh test  #对test文件执行sed.sh指令
DEVICE=eno16777736 
BOOTPROTO=static 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 
GATEWAY=192.168.0.254 
ONBOOT=yes
  • 执行多指令使用的方法
[[email protected] ~]# sed 's/yes/no/;s/static/dhcp/' test  #使用分号隔开
DEVICE=eno16777736 
BOOTPROTO=dhcp 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 

GATEWAY=192.168.0.254 

ONBOOT=no
[[email protected] ~]# sed -e 's/yes/no/' -e 's/static/dhcp/' test  #用-e选项
DEVICE=eno16777736 
BOOTPROTO=dhcp 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 

GATEWAY=192.168.0.254 

ONBOOT=no
[[email protected] ~]# sed '  #利用分行
> s/yes/no/
> s/static/dhcp/
> ' test
DEVICE=eno16777736 
BOOTPROTO=dhcp 
IPADDR=192.168.0.1 
NETMASK=25.255.255.0 

GATEWAY=192.168.0.254 

ONBOOT=no

操作地址匹配范例

[[email protected] ~]# sed -n '1~2p' test  #打印文件奇数行
DEVICE=eno16777736 
IPADDR=192.168.0.1 


[[email protected] ~]# sed '2,8d' test  #删除2~8行之间所有行
DEVICE=eno16777736

Sed常用指令

指 令功 能
s替换
d删除
a追加
i插入
c更改
l打印(显示非打印字符)
y按字符转换
L打印(不显示非打印字符)
p打印
r读入文件内容
w保存至文件
q退出

指令范例

范例1

[[email protected] ~]# cat neko.html 
<html>
<title>Neko Site</title>
<body>Cute Neko<body>
</html>
  • 如何将neko.html文件中第二个"body"替换成"/body"呢?
#编写一个sed.sh脚本:
[[email protected] ~]# cat sed.sh 
/body/{
s//\/body/2
}

依据上表可得s代表的是替换指令,不需要替换所有body,只需要替换第二个。

执行这个脚本:

[[email protected] ~]# sed -f sed.sh neko.html 
<html>
<title>Neko Site</title>
<body>Cute Neko</body>
</html>

范例2

[[email protected] ~]# cat neko.html 
<html>
<title>Neko Site</title>
<body>
h1Cute Nekoh1
h2Kawaii Nekoh2
h3Yasashi Nekoh3
</body>
</html>
  • "h1"、"h2"和"h3"分别表示一级标题、二级标题和三级标题,但是文件中缺少"<>"和"</>",如何使用Sed对文件进行修改纠正呢?
#编写一个sed.sh脚本
[[email protected] ~]# cat sed.sh 
/h[0-9]/{
s//\<&\>/1
s//\<\/&\>/2
}

在以上sed脚本中,我们要匹配的是h后跟一个数字的行,将h与数字替换成带有<>和</>的行,也就是将h[0-9]替换为<&>和</&>,&就是前面要替换的内容;其中第一行指令只替换第一个h1,h2和h3;第二行替换第二个h1,h2和h3。

运行脚本:

[[email protected] ~]# sed -f sed.sh neko.html 
<html>
<title>Neko Site</title>
<body>
<h1>Cute Neko</h1>
<h2>Kawaii Neko</h2>
<h3>Yasashi Neko</h3>
</body>
</html>

范例3

[[email protected] ~]# cat network 
DEVICE=eno16777736
ONBOOT=yes
BOOTPROTO=static

IPADDR=192.168.0.1
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
  • 如何删除文件中的空白行?
#编写一个sed.sh脚本
[[email protected] ~]# cat sed.sh 
/.*/{
/^$/d
}

匹配任意多个字符,如果匹配不到字串的结尾则为空,需要被删除。

运行结果:

[[email protected] ~]# sed -f sed.sh network 
DEVICE=eno16777736
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.1
NETMAST=255.255.255.0
GATEWAY=192.168.0.254

范例4

[[email protected] ~]# cat network 
DEVICE=eno1245523
ONBOOT=yes
BOOTPROTO=static
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
  • 在BOOTPROTO行下添加一行IPADDR项,内容为192.168.31.125。
[[email protected] ~]# sed '/BOOTPROTO=static/a IPADDR=192.468.31.125' network 
DEVICE=eno1245523
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.468.31.125
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
  • 在NETMAST行的前一行添加IPADDR项,内容为192.168.31.125。
[[email protected]Hyui-VM ~]# sed '/NETMAST/i IPADDR=192.468.31.125' network 
DEVICE=eno1245523
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.468.31.125
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
  • 将ONBOOT的值改为no。
[[email protected] ~]# sed '/ONBOOT/c ONBOOT=no' network 
DEVICE=eno1245523
ONBOOT=no
BOOTPROTO=static
NETMAST=255.255.255.0
GATEWAY=192.168.0.254

范例5

[[email protected] ~]# cat network 
DEVICE=eno16777736
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.1
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
  • 将前两行显示非打印字符。
[[email protected] ~]# sed -n '1,2l' network 
DEVICE=eno16777736$
ONBOOT=yes$
  • 显示一二行内容,不显示非显示字符。
[[email protected] ~]# sed -n '1,2p' network 
DEVICE=eno16777736
ONBOOT=yes

范例6

[[email protected] ~]# cat network 
DEVICE=eno16777736
onboot=yes
BOOTPROTO=static
ipaddr=192.168.0.1
nEtMaSt=255.255.255.0
GaTeWaY=192.168.0.254
  • 将文件中所有英文字符转为大写。
#编写sed脚本
[[email protected] ~]# cat sed.sh 
/.*/{
/.*/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
}

匹配文件中的全部字符,将全部英文字符替换成大写。

运行结果:

[[email protected] ~]# sed -f sed.sh network 
DEVICE=ENO16777736
ONBOOT=YES
BOOTPROTO=STATIC
IPADDR=192.168.0.1
NETMAST=255.255.255.0
GATEWAY=192.168.0.254

范例7

#文件1
[[email protected] ~]# cat username.txt 
HoshinoRinne
HoshinoAoba
HoshinoHibiki
#文件2
[[email protected] ~]# cat useremail.txt 
[email protected]
[email protected]
[email protected]
  • 先读取username.txt内容,后读取useremail.txt内容。
[[email protected] ~]# cat sed.sh 
/.*/{
$r useremail.txt
}

直接读入useremail.txt文件的内容。

运行结果:

[[email protected] ~]# sed -f sed.sh username.txt 
HoshinoRinne
HoshinoAoba
HoshinoHibiki
[email protected]
[email protected]
[email protected]

Sed高级应用

多行操作Next

  • Next指令可以通过读取新的输入行,将它追加至模式空间的现有内容之后,来创建多行模式空间。

举例1:

[[email protected] ~]# cat message.txt 
Name:Alice,
Mail:[email protected]
Name:Tim,
Mail:[email protected]
  • 使用Next指令编写sed脚本:
[[email protected] ~]# cat sed.sh 
#n
/Name/{
N
L
}
  • 其中,"#n"意为屏蔽自动输出,不使用的话会重复显示文件原内容和操作结果。"N"为Next指令。"L"为打印(不显示非打印字符)。
  • 运行结果如下:
[[email protected] ~]# sed -f sed.sh message.txt 
Name:Alice, Mail:[email protected]
Name:Tim, Mail:[email protected]

举例2:

[[email protected] ~]# cat num.txt 
111
222
222
222
333
  • 使用Next编写sed脚本:
[[email protected] ~]# cat sed.sh 
#n
/222/{
N
l
}
  • 其中先匹配222内容,匹配到以后立即读取下一行内容,使用"l"会打印非打印字符。
  • 运行结果:
[[email protected] ~]# sed -f sed.sh num.txt 
222\n222$
222\n333$
  • 其中"n"意为换行符。

多行操作Print

  • 多行打印Print指令为"P",其与小写"p"的区别为:P只输出多行模式空间中的第一部分,直到第一个插入的n换行符。

举例:

[[email protected] ~]# cat word.txt 
aaa
bbb
ccc
ddd
eee
fff
  • 不使用打印
[[email protected] ~]# sed '/.*/N' word.txt 
aaa
bbb
ccc
ddd
eee
fff
  • 这个命令只使用了Next指令读取下一行,新旧内容都是直接使用"n"分隔,读取下一行后没有任何后续指令,所以会输出源文件所有内容。
  • 使用L打印:
[[email protected] ~]# sed '/.*/N;L' word.txt 
aaa bbb
aaa
bbb
ccc ddd
ccc
ddd
eee fff
eee
fff
  • L打印表示显示模式空间的内容,而sed输出后会把源文件内容表示出来。
  • 使用P打印:
[[email protected] ~]# sed '/.*/N;P' word.txt 
aaa
aaa
bbb
ccc
ccc
ddd
eee
eee
fff
  • P打印为打印模式空间中第一部分内容直到n结尾,空间模式内容为aaanbbb的话就仅打印aaa。
  • 使用p打印:
[[email protected] ~]# sed '/.*/N;p' word.txt 
aaa
bbb
aaa
bbb
ccc
ddd
ccc
ddd
eee
fff
eee
fff
  • 使用p打印时遇到n会直接回车换行。

多行操作Delete

[[email protected] ~]# cat word.txt 
aaa
bbb
ccc
ddd
eee
fff
  • 使用Delete指令:
[[email protected] ~]# sed '/aaa/d' word.txt 
bbb
ccc
ddd
eee
fff

Hold(h,H), Get(g,G)

  • Sed拥有一个hole space(保持空间)的缓冲区,模式空间可以复制内容到保持空间,保持空间内容也可以复制到模式空间。
  • Hold(h|H) 将模式空间内容复制或追加到保持空间
  • Get(g|G) 将保持空间内容复制或追加到模式空间
  • Exchange(x) 交换保持空间与模式空间的内容

举例:

[[email protected] ~]# cat word.txt 
aaa
bbb
ccc
ddd
  • 编写sed脚本:
[[email protected] ~]# cat sed.sh 
/aaa/{
h
d
}
/ccc/{
G
}
  • 脚本中首先匹配aaa字串,将aaa字串复制到保持空间,然后删除aaa字串;随后匹配ccc字串,将保持空间的内容追加到ccc字串的下一行。
  • 运行结果:
[[email protected] ~]# sed -f sed.sh word.txt 
bbb
ccc
aaa
ddd