CentOS7 自动化运维基础 Sed编辑
hyui 2/19/2020 linuxBash
# 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表示替换指令
[root@Hyui-VM ~]# cat test
DEVICE=eno16777736
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=25.255.255.0
GATEWAY=192.168.0.254
ONBOOT=yes
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# sed '3,4d' test #删除三四行的内容
DEVICE=eno16777736
BOOTPROTO=static
GATEWAY=192.168.0.254
ONBOOT=yes
- 如果出现不确定的行号,更多可以使用正则表达式确定操作对象
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# sed '/^GATEWAY/d' test #删除以GATEWAY开头的行
DEVICE=eno16777736
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=25.255.255.0
ONBOOT=yes
- 可以将sed指令写入脚本文件中,通过sed -f来选取
[root@Hyui-VM ~]# cat sed.sh
#This is sed.sh
/^$/d
[root@Hyui-VM ~]# 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
- 执行多指令使用的方法
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# 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
# 操作地址匹配范例
[root@Hyui-VM ~]# sed -n '1~2p' test #打印文件奇数行
DEVICE=eno16777736
IPADDR=192.168.0.1
[root@Hyui-VM ~]# sed '2,8d' test #删除2~8行之间所有行
DEVICE=eno16777736
# Sed常用指令
指 令 | 功 能 |
---|---|
s | 替换 |
d | 删除 |
a | 追加 |
i | 插入 |
c | 更改 |
l | 打印(显示非打印字符) |
y | 按字符转换 |
L | 打印(不显示非打印字符) |
p | 打印 |
r | 读入文件内容 |
w | 保存至文件 |
q | 退出 |
# 指令范例
# 范例1
[root@Hyui-VM ~]# cat neko.html
<html>
<title>Neko Site</title>
<body>Cute Neko<body>
</html>
- 如何将neko.html文件中第二个"body"替换成"/body"呢?
#编写一个sed.sh脚本:
[root@Hyui-VM ~]# cat sed.sh
/body/{
s//\/body/2
}
依据上表可得s代表的是替换指令,不需要替换所有body,只需要替换第二个。
执行这个脚本:
[root@Hyui-VM ~]# sed -f sed.sh neko.html
<html>
<title>Neko Site</title>
<body>Cute Neko</body>
</html>
# 范例2
[root@Hyui-VM ~]# cat neko.html
<html>
<title>Neko Site</title>
<body>
h1Cute Nekoh1
h2Kawaii Nekoh2
h3Yasashi Nekoh3
</body>
</html>
- "h1"、"h2"和"h3"分别表示一级标题、二级标题和三级标题,但是文件中缺少"<>"和"</>",如何使用Sed对文件进行修改纠正呢?
#编写一个sed.sh脚本
[root@Hyui-VM ~]# cat sed.sh
/h[0-9]/{
s//\<&\>/1
s//\<\/&\>/2
}
在以上sed脚本中,我们要匹配的是h后跟一个数字的行,将h与数字替换成带有<>和</>的行,也就是将h[0-9]替换为<&>和</&>,&就是前面要替换的内容;其中第一行指令只替换第一个h1,h2和h3;第二行替换第二个h1,h2和h3。
运行脚本:
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# 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脚本
[root@Hyui-VM ~]# cat sed.sh
/.*/{
/^$/d
}
匹配任意多个字符,如果匹配不到字串的结尾则为空,需要被删除。
运行结果:
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# cat network
DEVICE=eno1245523
ONBOOT=yes
BOOTPROTO=static
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
- 在BOOTPROTO行下添加一行IPADDR项,内容为192.168.31.125。
[root@Hyui-VM ~]# 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。
[root@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。
[root@Hyui-VM ~]# sed '/ONBOOT/c ONBOOT=no' network
DEVICE=eno1245523
ONBOOT=no
BOOTPROTO=static
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
# 范例5
[root@Hyui-VM ~]# cat network
DEVICE=eno16777736
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.1
NETMAST=255.255.255.0
GATEWAY=192.168.0.254
- 将前两行显示非打印字符。
[root@Hyui-VM ~]# sed -n '1,2l' network
DEVICE=eno16777736$
ONBOOT=yes$
- 显示一二行内容,不显示非显示字符。
[root@Hyui-VM ~]# sed -n '1,2p' network
DEVICE=eno16777736
ONBOOT=yes
# 范例6
[root@Hyui-VM ~]# cat network
DEVICE=eno16777736
onboot=yes
BOOTPROTO=static
ipaddr=192.168.0.1
nEtMaSt=255.255.255.0
GaTeWaY=192.168.0.254
- 将文件中所有英文字符转为大写。
#编写sed脚本
[root@Hyui-VM ~]# cat sed.sh
/.*/{
/.*/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
}
匹配文件中的全部字符,将全部英文字符替换成大写。
运行结果:
[root@Hyui-VM ~]# 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
[root@Hyui-VM ~]# cat username.txt
HoshinoRinne
HoshinoAoba
HoshinoHibiki
#文件2
[root@Hyui-VM ~]# cat useremail.txt
hoshinorinne@hyui.xyz
hoshinoaoba@hyui.xyz
hoshinohibiki@hyui.xyz
- 先读取username.txt内容,后读取useremail.txt内容。
[root@Hyui-VM ~]# cat sed.sh
/.*/{
$r useremail.txt
}
直接读入useremail.txt文件的内容。
运行结果:
[root@Hyui-VM ~]# sed -f sed.sh username.txt
HoshinoRinne
HoshinoAoba
HoshinoHibiki
hoshinorinne@hyui.xyz
hoshinoaoba@hyui.xyz
hoshinohibiki@hyui.xyz
# Sed高级应用
# 多行操作Next
- Next指令可以通过读取新的输入行,将它追加至模式空间的现有内容之后,来创建多行模式空间。
# 举例1:
[root@Hyui-VM ~]# cat message.txt
Name:Alice,
Mail:Alice@hyui.xyz
Name:Tim,
Mail:Tim@hyui.xyz
- 使用Next指令编写sed脚本:
[root@Hyui-VM ~]# cat sed.sh
#n
/Name/{
N
L
}
- 其中,"#n"意为屏蔽自动输出,不使用的话会重复显示文件原内容和操作结果。"N"为Next指令。"L"为打印(不显示非打印字符)。
- 运行结果如下:
[root@Hyui-VM ~]# sed -f sed.sh message.txt
Name:Alice, Mail:Alice@hyui.xyz
Name:Tim, Mail:Tim@hyui.xyz
# 举例2:
[root@Hyui-VM ~]# cat num.txt
111
222
222
222
333
- 使用Next编写sed脚本:
[root@Hyui-VM ~]# cat sed.sh
#n
/222/{
N
l
}
- 其中先匹配222内容,匹配到以后立即读取下一行内容,使用"l"会打印非打印字符。
- 运行结果:
[root@Hyui-VM ~]# sed -f sed.sh num.txt
222\n222$
222\n333$
- 其中"\n"意为换行符。
# 多行操作Print
- 多行打印Print指令为"P",其与小写"p"的区别为:P只输出多行模式空间中的第一部分,直到第一个插入的\n换行符。
# 举例:
[root@Hyui-VM ~]# cat word.txt
aaa
bbb
ccc
ddd
eee
fff
- 不使用打印
[root@Hyui-VM ~]# sed '/.*/N' word.txt
aaa
bbb
ccc
ddd
eee
fff
- 这个命令只使用了Next指令读取下一行,新旧内容都是直接使用"\n"分隔,读取下一行后没有任何后续指令,所以会输出源文件所有内容。
- 使用L打印:
[root@Hyui-VM ~]# sed '/.*/N;L' word.txt
aaa bbb
aaa
bbb
ccc ddd
ccc
ddd
eee fff
eee
fff
- L打印表示显示模式空间的内容,而sed输出后会把源文件内容表示出来。
- 使用P打印:
[root@Hyui-VM ~]# sed '/.*/N;P' word.txt
aaa
aaa
bbb
ccc
ccc
ddd
eee
eee
fff
- P打印为打印模式空间中第一部分内容直到\n结尾,空间模式内容为aaa\nbbb的话就仅打印aaa。
- 使用p打印:
[root@Hyui-VM ~]# sed '/.*/N;p' word.txt
aaa
bbb
aaa
bbb
ccc
ddd
ccc
ddd
eee
fff
eee
fff
- 使用p打印时遇到\n会直接回车换行。
# 多行操作Delete
[root@Hyui-VM ~]# cat word.txt
aaa
bbb
ccc
ddd
eee
fff
- 使用Delete指令:
[root@Hyui-VM ~]# 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) 交换保持空间与模式空间的内容
# 举例:
[root@Hyui-VM ~]# cat word.txt
aaa
bbb
ccc
ddd
- 编写sed脚本:
[root@Hyui-VM ~]# cat sed.sh
/aaa/{
h
d
}
/ccc/{
G
}
- 脚本中首先匹配aaa字串,将aaa字串复制到保持空间,然后删除aaa字串;随后匹配ccc字串,将保持空间的内容追加到ccc字串的下一行。
- 运行结果:
[root@Hyui-VM ~]# sed -f sed.sh word.txt
bbb
ccc
aaa
ddd