赞
踩
Sed是一款流编辑工具,用来对文本进行过滤与替换操作,特别是当你想要对几十个配置文件做统一修改时,你会感受到Sed的魅力!Sed通过一次仅读取一行内容来对某些指令进行处理后输出,所以Sed更适合于处理大数据文件。首先,Sed通过文件或管道读取文件内容,但Sed默认并不直接修改源文件,而是将读入的内容复制到缓冲区中,我们称之为模式空间(pattern space),所有的指令操作都是在模式空间中进行的,然后Sed根据相应的指令对模式空间中的内容进行处理并输出结果,默认输出至标准输出(即屏幕上)。Sed的工作流程如下图所示:
Sed从文件中读取数据,如果没有输入文件,则默认对标准输入进程数据进行处理,脚本指令是第一个非“-”开头的参数,具体语法格式如下:
sed [选项]...{脚本指令} [输入文件]
选项 | 含义 |
--version | 显示sed版本 |
--help | 显示帮助文档 |
-n,--quit,--silent | 静默输出,默认情况下,sed程序在所有的脚本指令执行完毕后,将自动打印模式空间中的内容,该选项可以屏蔽自动打印。 |
-e script | 允许多个脚本指令被执行 |
-f script-file | 从文件中读取脚本指令,对编写自动脚本程序很实用。 |
-i,--in-place | 慎用,该选项将直接修改源文件 |
-l,N | 该选项指令l指令可以输出的行长度,l指令为输出非打印字符。 |
--posix | 禁用GNU sed扩展功能 |
-r | 在脚本指令中使用扩展正则表达式 |
-s,--separate | 默认情况下,sed将把输入的多个文件名作为一个长的连续的输入流,而GNU sed则允许把它们当作单独的文件 |
-u,--unbuffered | 最低限度的缓存输入和输出 |
1.基本格式规范
Sed通过特定的脚本指令对文件进行处理,这里就简单介绍几个脚本指令操作作为Sed程序的规范。a,append表示追加指令;i,insert表示插入指令;d,delete表示删除指令;s,substitution表示替换指令。Sed脚本指令的基本格式是:[地址]命令(有些命令仅可以对一行操作,有些可以对多行操作),命令也可以用花括号进行组合,是命令序列可以作用于同一个地址:
address{
command1
command2
command3
}
注意:第一个命令可以和左花括号在同一行,但右花括号必须单独处于一行。此外,命令后添加空格会产生错误。
下面的test.txt为操作样本源文件(注意有若干空白行),介绍Sed的用法:
[root@andrew Andrew]# cat -n test.txt
1 DEVICE=eno16777736
2 BOOTPROTO=static
3 IPADDR=192.168.0.1
4 NETMASK=255.255.255.0
5
6 GATEWAY=192.168.0.254
7
8 ONBOOT=yes
第二行后追加TYPE=Ethernet:
[root@andrew Andrew]# sed '2a TYPE=Ethernet' test.txt
DEVICE=eno16777736
BOOTPROTO=static
TYPE=Ethernet
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=yes
第三行前追加TYPE=Network:
[root@andrew Andrew]# sed '3i TYPE=Network' test.txt
DEVICE=eno16777736
BOOTPROTO=static
TYPE=Network
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=yes
将样本文件中的所有yes替换为no:
[root@andrew Andrew]# sed 's/yes/no/g' test.txt
DEVICE=eno16777736
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=no
以上大多数操作指令都依据行号定位操作对象(地址),如2a即第二行后追加。实际工作中,可能大多数情况你并不确定你要操作对象(地址)的行号,这时更多的会使用正则表达式确定操作对象(地址)。下面是使用正则表达式定位操作行的示例:
匹配到包含ONBOOT的行,并在其后添加TYPE=Ethernet:
[root@andrew Andrew]# sed '/ONBOOT/a TYPE=Ethernet' test.txt
DEVICE=eno16777736
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=yes
TYPE=Ethernet
匹配GATEWAY开始的行,并删除该行:
[root@andrew Andrew]# sed '/^GATEWAY/d' test.txt
DEVICE=eno16777736
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=255.255.255.0
ONBOOT=yes
另外,我们的操作指令可以写入到脚本文件中,并通过sed的-f选项读取,脚本文件中的注释行是以#开始的行,如果#后面的字符为n,则屏蔽Sed程序的自动输出功能,等同于命令选项-n。创建一个sed脚本,内容如下:
[root@andrew Andrew]# cat sed.sh
#This is a test sed command
#脚本内容为匹配到空白行后,删除该行
/^$/d
对test.txt文件执行sed.sh脚本指令:
[root@andrew Andrew]# sed -f sed.sh test.txt
DEVICE=eno16777736
BOOTPROTO=static
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=yes
而当你需要执行多个指令时,可以使用以下三种方法:
(1)使用分号隔开指令
[root@andrew Andrew]# sed 's/yes/no/;s/static/dhcp/' test.txt
DEVICE=eno16777736
BOOTPROTO=dhcp
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=no
(2)使用-e选项
[root@andrew Andrew]# sed -e 's/yes/no/' -e 's/static/dhcp/' test.txt
DEVICE=eno16777736
BOOTPROTO=dhcp
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=no
(3)利用分行
[root@andrew Andrew]# sed '
> s/yes/no/
> s/static/dhcp/' test.txt
DEVICE=eno16777736
BOOTPROTO=dhcp
IPADDR=192.168.0.1
NETMASK=255.255.255.0
GATEWAY=192.168.0.254
ONBOOT=no
然而在命令行上输入过长的指令是愚蠢的,这时需要使用-f选项指定sed脚本文件,在脚本文件中可以包含多行指令,而且也便于修改。
2.操作地址匹配范例
通过以上范例不难发现,我们编写的脚本指令需要指定一个地址来决定操作范围,如果不指定,则默认对文件的所有行进行操作。如:sed ‘d’ test.txt将删除test.txt的所有行,而‘2d’则仅删除第二行。Sed为我们提供了以下这些方式来确定需要操作地址的范围。
-number | 指定输入文件的唯一行号 |
-first~step | 指定以first开始,并指定操作步长为step,如1、2指定第一行、第三行、第五行……为操作地址。2~5指定第二行开始,每5行匹配一次操作地址 |
$ | 匹配文件的最后一行 |
/regexp/ | //中间包含的是正则表达式,通过正则表达式匹配操作地址。如果//中正则表达式为空,匹配最近一次正则表达式的匹配地址,后面会有范例 |
\cregexpc | \c和c之间匹配正则表达式,c字符可以使用任意字符代替 |
addr1,addr2 | 匹配从操作地址1到操作地址2的所有行 |
addr1,+N | 匹配地址1以及后面的N行内容 |
打印文件的奇数行:
[root@andrew Andrew]# cat -n test.txt
1 DEVICE=eno16777736
2 BOOTPROTO=static
3 IPADDR=192.168.0.1
4 NETMASK=255.255.255.0
5
6 GATEWAY=192.168.0.254
7
8 ONBOOT=yes
[root@andrew Andrew]# sed -n '1~2p' test.txt
DEVICE=eno16777736
IPADDR=192.168.0.1
删除2~8之间的所有行:
root@andrew Andrew]# cat -n test.txt
1 DEVICE=eno16777736
2 BOOTPROTO=static
3 IPADDR=192.168.0.1
4 NETMASK=255.255.255.0
5
6 GATEWAY=192.168.0.254
7
8 ONBOOT=yes
[root@andrew Andrew]# sed '2,8d' test.txt
DEVICE=eno16777736
1.Sed常用指令汇总
下表给出了常用sed脚本指令的说明,下面分别看看每个指令的详细用法:
指令 | 功能 | 指令 | 功能 |
-s | 替换 | -d | 删除 |
-a | 追加 | -i | 插入 |
-c | 更改 | -l | 打印(显示非打印字符) |
-y | 按字符转换 | -L | 打印(不显示非打印字符) |
-p | 打印 | -r | 读入文件内容 |
-w | 保存至文件 | -q | 退出 |
2.部分指令详解
(1)替换指令(s,Substitution)
指令格式:[address]s/pattern/replacement/flags
address为操作地址,s为替换指令,/pattern/匹配需要替换的内容,/replacement为替换的新内容。Flags标记可以是:
Flags标记 | 含义 |
-n | 1-512之间的数字,表示对模式空间中指定模式的第n次出现进行替换。如一行中有3个A,而只想替换第二个A。 |
-g | 对模式空间的所有匹配进行全局更改。没有g则只有第一次匹配被替换,如一行中有3个A,则仅替换第一个A。 |
-p | 打印模式空间的内容 |
-w file | 将模式空间的内容写到文件file中 |
replacement为字符串,用来替换与正则表达式匹配的内容。在replace部分,只有下列字符有特殊含义:
& | 用正则表达式匹配的内容进行替换 |
\n | 匹配第n个子串,该子串之前在pattern中用\(\)指定 |
\ | 转义(转义替换部分包含:&、\等) |
(2)删除指令(d,delete)
删除指令用于删除匹配的行,而且删除命令还会改变sed脚本中命令的执行顺序。因为匹配的行一旦删除,模式空间将变为“空”,自然不会再执行sed脚本后续的命令。删除命令将导致读取新的输入行(下一行),而sed脚本中的命令则从头开始重新执行。需要注意的是,删除时是删除整行,而不只是删除匹配的内容(如果删除匹配的内容,可以使用替换)
(3)转换指令(y)
按字符转换(Transform)的语法格式为:[address]y/source-chars/dest-chars,其中,[address]用来定位需要修改的行,source-chars为需要被修改的字符,dest-chars为准备替换的字符。
3.Sed脚本指令范例
(1)范例1
范例1所使用的样本文件为:
-
[root@andrew Andrew]# cat test.txt
-
<html>
-
<title>First Web
</title>
-
<body>Hello the World!
<body>
-
</html>
范例1:将样本文件中第二个<body>替换为</body>
编写sed脚本,替换与行匹配相同的内容,即将body替换为/body,但仅替换第二个body为/body。
-
[root@andrew Andrew]
# cat sed.sh
-
/body/{
-
s//\/body/
2
-
}
执行sed程序的结果如下:
-
[root@andrew Andrew]# sed -f sed.sh test.txt
-
<html>
-
<title>First Web
</title>
-
<body>Hello the World!
</body>
-
</html>
(2)范例2
范例2使用的样本文件为:
-
[root@andrew Andrew]# cat test.txt
-
<html>
-
<title>First Web
</title>
-
<body>
-
h1Helloh1
-
h2Helloh2
-
h3Helloh3
-
</body>
-
</html>
范例2:给所有第一个的h1,h2等添加<>,第二个h1,h2添加</>.
编写sed脚本为:
-
[root@andrew Andrew]
# cat sed.sh
-
/h[
0-
9]/{
-
s//\<&\>/
1
-
s//\<\/&\>
/2
-
}
说明:匹配h紧跟一个数字的行,替换与上一行中匹配内容相同的内容,即将h[0-9]替换为<&>,其中&为前面要替换的内容。第一条s替换指令仅替换第一个h1,h2…,第二条s替换指令用来替换第二个h1,h2...
执行sedc程序的结果如下:
-
[root@andrew Andrew]# sed -f sed.sh test.txt
-
<html>
-
<title>First Web
</title>
-
<body>
-
<h1>Hello
</h1>
-
<h2>Hello
</h2>
-
<h3>Hello
</h3>
-
</body>
-
</html>
技巧:关于‘s///’命令的另一个妙处是:‘/’分隔符有许多替换方案,如果规则表达式或替换字符串中有许多斜杠,则可以通过在‘s’之后指定一个不同的字符来更改分隔符。示例:sed -e ‘s:/usr/local:/usr:g’ mylist.txt此时是替换分隔符,sed会将/usr/local替换为/usr.
(3)范例3
范例3所使用的样本文件为(注意有空白行):
-
[
root@andrew Andrew]
# cat test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
-
IPADDR=
192.168
.0
.1
-
NETMASK=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
范例3:删除文件中的空白行。
编写sed脚本为:
-
[root@andrew Andrew]
# cat sed.sh
-
/.*
/{
-
/^$/d
-
}
执行sed程序的结果如下:
-
[
root@andrew Andrew]
# sed -f sed.sh test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
IPADDR=
192.168
.0
.1
-
NETMASK=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
(4)范例4-范例6
范例4-范例7所使用的样本文件为:
-
[
root@andrew Andrew]
# cat test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
NETMASK=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
范例4:在static行后添加一行,内容为IPADDR=192.168.0.1。
-
[root@andrew Andrew]
# sed '/static/a IPADDR=192.168.0.1' test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
IPADDR=
192.168
.0
.1
-
NETMASK=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
范例5:在匹配NETMASK的行前插入内容IPADDR=192.168.0.1。
-
[
root@andrew Andrew]
# sed '/NETMASK/i IPADDR=192.168.0.1' test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
IPADDR=
192.168
.0
.1
-
NETMASK=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
范例6:将包含ONBOOT行的内容更改为ONBOOT=no
-
[root@andrew Andrew]
# sed '/ONBOOT/c ONBOOT=no' test.txt
-
DEVICE=eno16777736
-
ONBOOT=
no
-
BOOTPROTO=static
-
NETMASK=
255.255.
255.0
-
GATEWAY=
192.168.
0.
254
(5)范例7和范例8
范例7和范例8所使用的样本文件为:
-
[
root@andrew Andrew]
# cat test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
netmask=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
范例7:将小写转换为大写
编写sed脚本为:
-
[root@andrew Andrew]
# cat sed.sh
-
/.*
/{
-
/netmask
/y/abcdefghijklmnopqrstuvwxyz
/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
-
}
执行sed程序的结果如下:
-
[
root@andrew Andrew]
# sed -f sed.sh test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
-
BOOTPROTO=
static
-
NETMASK=
255.255
.255
.0
-
GATEWAY=
192.168
.0
.254
范例8:显示第一、第二行的内容
打印(p):作用类似于l(列印),但不显示非显示字符,一般与-n配合使用
-
[root@andrew Andrew]
# sed -n '1,2p' test.txt
-
DEVICE=eno16777736
-
ONBOOT=yes
(6)范例9和范例10
范例9和范例10所使用的样本文件为:
-
[root@andrew Andrew]
# cat name.txt
-
Jacob
-
Tom
-
Jerry
-
[root@andrew Andrew]
# cat mail.txt
-
jacob@gmail.com
-
tom@gmail.com
-
jerry@gmail.com
范例9:先读取name.txt文件内容,再读取mail.txt文件内容。
编写sed脚本为:
-
[root@andrew Andrew]
# cat sed.sh
-
/.*/{
-
$r mail.txt
-
}
执行sed程序的结果如下:
-
[root@andrew Andrew]#
sed
-f
sed
.sh
name
.txt
-
Jacob
-
Tom
-
Jerry
-
jacob@
gmail.
com
-
tom@gmail.com
-
jerry@gmail.com
范例10:显示name.txt内容的前两行内容后退出sed指令。
-
[root@andrew Andrew]# sed
'2q'
name.txt
-
Jacob
-
Tom
正常的Sed数据处理流程是读取文档的一行至模式空间,然后对该行应用相应的Sed指令,当指令完成后输出该行并清空模式空间,依次循环读入文档的下一行数据,直至文档数据结尾。然而在真实环境中的数据可能不会那么有规律,有时我们会把数据分多行写入文档,如:
-
姓名:张三,
-
邮箱:zhangsan
@gmail.com
-
姓名:李四,
-
邮箱:lisi
@gmain.com
从上面的模板文件中可以看出,实际上每两行为一条完整的记录,而此时如果需要使用Sed对该文档进行处理,就需要对Sed工作流程进行人工干预。
1.多行操作Next
Next(N)指令通过读取新的输入行,并将它追加至模式空间的现有内容之后,来创建多行模式空间。模式空间的最初内容与新的输入行之间用换行符分割。在模式空间中插入的换行符可以使用\n匹配。
范例1:使用的样本文件为:
-
[root@andrew Andrew]#
cat
test
.txt
-
Name
:Tom
-
Mail
:Tom@
gmail.
com
-
Name:Jerry
-
Mail:Jerry@gmail.com
编写Sed指令脚本如下(读取样本文件内容至模式空间,当读取的内容与Name匹配时,立刻读取下一行内容,再输出模式空间中的内容,#n用来屏蔽自动输出)。
-
[root@andrew Andrew]
# cat sed.sh
-
#n
-
/Name/{
-
N
-
L
-
}
运行脚本结果如下:
-
[root@andrew Andrew]#
sed
-f
sed
.sh
test
.txt
-
Name
:Tom
Mail
:Tom@
gmail.
com
-
Name:Jerry Mail:Jerry@gmail.com
范例2:使用的样本文件为:
-
[root@andrew Andrew]#
cat
test
.txt
-
111
-
222
-
222
-
222
-
333
编写Sed指令脚本(读取样本文件内容至模式空间,当读取的内容与222匹配时,立刻读取下一行内容,再输出模式空间中的内容,小写的l会打印非打印字符)。
-
[root@andrew Andrew]
# cat sed.sh
-
#n
-
/
222/{
-
N
-
l
-
}
运行脚本结果如下:
-
[root@andrew Andrew]
# sed -f sed.sh test.txt
-
222\n222$
-
222\n333$
2.多行操作Print
Print(P)即多行打印P,它与打印p稍有不同,前者仅输出多行模式空间中的第一部分,直到第一个插入的\n换行符为止。综合范例所使用的样本文件为:
-
[root@andrew Andrew]#
cat
test
.txt
-
aaa
-
bbb
-
ccc
-
ddd
-
eee
-
fff
下面通过多条sed命令对比不同打印方式的差别,输出结果如下所示:
-
[root@andrew Andrew]
# sed '/.*/N' test.txt
-
aaa
-
bbb
-
ccc
-
ddd
-
eee
-
fff
-
-
-
[root@andrew Andrew]
# sed '/.*/N;L' test.txt
-
aaa bbb
-
aaa
-
bbb
-
ccc ddd
-
ccc
-
ddd
-
eee fff
-
eee
-
fff
-
-
-
[root@andrew Andrew]
# sed '/.*/N;P' test.txt
-
aaa
-
aaa
-
bbb
-
ccc
-
ccc
-
ddd
-
eee
-
eee
-
fff
-
-
-
[root@andrew Andrew]
# sed '/.*/N;p' test.txt
-
aaa
-
bbb
-
aaa
-
bbb
-
ccc
-
ddd
-
ccc
-
ddd
-
eee
-
fff
-
eee
-
fff
第一个sed命令使用了N读取下一行,新读取的内容与原有内容直接使用\n分隔。但sed读取下一行后没有任何后续指令,所以sed自动输出,即输出源文件的所有内容。
第二个sed命令使用了N读取下一行,L表示显示模式空间的内容,即aaa bbb,同时sed命令的自动输出功能会把源文件内容显示出来,即aaa,bbb。以此类推,sed继续读取第三行ccc,并使用N将ddd追加至行尾,使用L显示模式空间的内容(不显示非打印字符),sed自动输出后再把源文件内容显示出来。
第三个sed命令使用N将下一行追加至行尾,现在模式空间中的内容为aaa\bbb,而P指令的作用是打印模式空间中的第一部分内容直到\n结尾,即仅打印aaa,这时sed的自动输出功能输出aaa,bbb(sed自动输出会将\n输出为换行)。依次类推,读取第三行ccc,N将ddd追加至行尾,P打印\n前的内容,同时sed命令也将自动输出。
第四个sed命令的原理类似于第三个sed命令,但p打印时,\n看作回车换行,所以打印出来的是aaa回车bbb.
3.多行删除操作Delete(D)
指令d为删除命令,其作用是删除模式空间中的内容并读入新的输入行,而如果sed在d指令后还有多条指令,则余下的指令将不再执行,而是返回第一条指令对新读入行进行处理。而多行删除指令D将删除模式空间中直到第一个插入的换行符(\n)前的这部份内容,它不会读入新的输入行,并返回sed脚本的顶端,使得剩余指令继续应用于模式空间中剩余的内容。
4.Hold(h,H),Get(g,G)
我们知道,模式空间是存放当前输入行的缓冲区。除此之外,Sed还有一个称为保持空间(hold space)的缓冲区。模式空间的内容可以复制到保持空间,保持空间的内容同样可以复制到模式空间,有一组Sed命令用于在两者之间移动数据。
Hold(h|H) | 将模式空间的内容复制或追加到保持空间 |
Get(g|G) | 将保持空间的内容复制或追加到模式空间 |
Exchange(x) | 交换保持空间与模式空间的内容 |
保留空间范例所使用的样本文件如下:
-
[root@andrew Andrew]#
cat
test
.txt
-
aaa
-
bbb
-
ccc
-
ddd
-
[root@andrew Andrew]
# cat sed.sh
-
/aaa/{
-
h
-
d
-
}
-
/ccc/{
-
G
-
}
-
[root@andrew Andrew]#
sed
-f
sed
.sh
test
.txt
-
bbb
-
ccc
-
aaa
-
ddd
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。