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

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 
[email protected]
[email protected]
[email protected]
  • 先读取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
[email protected]
[email protected]
[email protected]

Sed高级应用

多行操作Next

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

    举例1:

    [root@Hyui-VM ~]# cat message.txt 
    Name:Alice,
    Mail:[email protected]
    Name:Tim,
    Mail:[email protected]
  • 使用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:[email protected]
    Name:Tim, Mail:[email protected]

    举例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