sed原理及使用

前言

环境:centos6.5html

sed版本:GNU sed version 4.2.1正则表达式

本文的代码都是在这个环境下验证的。shell

1、简介

sed(Stream Editor)意为流编辑器,是Unix常见的命令行程序。是Bell实验室的 Lee E.McMahon 在1973年到1974年之间开发完成,目前能够在大多数操做系统中使用。编程

sed的出现是做为grep的一个继任者,由于grep只能简单的进行查找和替换,可是考虑还可能会有删除等各类需求,McMahon 开发了一个更具通用性的工具。sed著名的语法规则包括使用 / 进行模式匹配,以及 s/// 来进行替代。与同期存在的工具ed一块儿,sed的语法影响了后来发展的 ECMAScript 和 Perl。GNU sed 添加了不少特性,包括著名的 in-place editing。vim

 

2、处理流程

要说明sed的处理流程,须要先提它的命令语法:centos

sed [options] script filename

通常来讲,sed是从stdin读取输入,而且将输出写出到stdout,可是filename被指定时,会从指定的文件中获取输入,输出能够重定向到另外的文件中。缓存

options指的是sed的命令行参数,比较有限,这个后面会说明。app

script是指须要对输入执行的一个或者多个操做指令,通常须要用单引号括起来,这样能够避免shell对特殊字符的处理。sed会依次读取输入文件的每一行到缓存中并应用script中指定的操做指令,所以而带来的变化并不会影响最初的文件(除非option加了-i参数)。编程语言

若是操做指令不少,为了避免影响可读性,能够将其写入文件,并用-f参数指定该文件:编辑器

sed -f scriptfile filename

不管是将操做指令经过命令行指定,仍是写入到文件中做为一个sed脚本,必须包含至少一个指令,不然用sed就没有意义了。

每条操做指令由patternprocedure两部分组成,顾名思义,pattern是匹配的规则,通常为用'/'分隔的正则表达式(也有多是行号,具体参见Sed命令地址匹配问题总结),而procedure则是一连串编辑命令(action)。 sed的处理流程,简化后是这样的:

  1. 读入新的一行内容到缓存空间;
  2. 从指定的操做指令中取出第一条指令,判断是否匹配pattern;
  3. 若是不匹配,则忽略后续的编辑命令,回到第2步继续取出下一条指令;
  4. 若是匹配,则针对缓存的行执行后续的编辑命令;完成后,回到第2步继续取出下一条指令;
  5. 当全部指令都应用以后,输出缓存行的内容;回到第1步继续读入下一行内容;
  6. 当全部行都处理完以后,结束;

具体流程如图所示:

simple_sed_process_flow_chart

 

3、命令选项options

根据上面提到的,咱们知道sed通常有两种语法形式:

sed [options] script filename
sed -f scriptfile filename

 

options经常使用的只有几个:

-n :使用安静(silent)模式。在通常 sed 的用法中,全部来自 STDIN 的数据通常都会被列出到终端上。但若是加上 -n 参数后,则只有通过sed 特殊处理的那一行(或者动做)才会被列出来。

-e :直接在命令列模式上进行 sed 的动做编辑;

-f :直接将 sed 的动做写在一个文件内, -f filename 则能够运行 filename 内的 sed 动做;

-r :sed 的动做支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)

-i :直接修改读取的文件内容,而不是输出到终端。

 

在这里,-e参数实际上不少时候会让人很迷惑。由于咱们平时使用sed的时候通常都不会加-e参数,譬如:

seq 6 | sed -n '1,2p'

它和下面的写法是等价的:

seq 6 | sed -ne '1,2p'

因此不少文章在说到-e这个参数的时候说能够省略,实际上,这种说法是不严谨的。sed发展到如今,已经有各类各样的版本,在有些版本中,-e参数是不能够省略的,可是咱们通常使用的都是GNU sed(gsed),因此在这种状况能够省略。-e参数最经常使用的场景是对同一文件做屡次修改,如:

seq 6 | sed -e 's/1/10/' -e 's/2/20/'

可是实际上多重编辑还有更简洁的写法,就是在同一个script中,用分号分割两句指令:

seq 6 | sed -e 's/1/10/;s/2/20/'

 

在处理流程部分,咱们提到,每条操做指令script通常由patternprocedure两部分组成,接下来讲说pattern和procedure。

 

4、pattern

1. 模式空间

回顾一下上面讲到的处理流程,咱们能够知道,sed是以行的方式来处理数据的,每一行被读入到一块缓存空间,该空间名为模式空间(pattern space)。所以sed操做的都是最初行的拷贝,同时后续的编辑命令都是应用到前面的命令编辑后输出的结果,因此编辑命令之间的顺序就显得格外重要。

让咱们来看一个很是简单的例子,将一段文本中的pig替换成cow,而且将cow替换成horse:

echo "pig and cow and horse" | sed 's/pig/cow/;s/cow/hores/'

初看起来好像没有问题,可是其实是错误的,最后输出其实是:

hores and cow and horse

缘由是第一个替换命令将全部的pig都替换成cow,紧接着的替换命令是基于前一个结果处理的,将全部的cow都替换成horse,形成的结果是所有的pig/cow都被替换成了horse,这样违背了咱们的初衷。在这种状况下,只须要调换下两个编辑命令的顺序:

echo "pig and cow and horse" | sed 's/cow/hores/;s/pig/cow/'

 

2. 模式空间的转换

sed只会缓存一行的内容在模式空间,这样的好处是sed能够处理大文件而不会有任何问题,不像一些编辑器由于要一次性载入文件的一大块内容到缓存中而致使内存不足。

例如,如今要把一段文本中的Unix System与UNIX System都要统一替换成The UNIX Operating System,所以咱们用两句替换命令来完成这个目的:

echo "the Unix System" | sed 's/Unix/UNIX/;s/UNIX System/UNIX Operating System/'
==> the UNIX Operating System

过程简单来讲以下:

1)读入一行内容到模式空间

2)应用第一条替换命令将Unix换成UNIX

3)模式空间的内容变为”the UNIX System”

4)应用第二条替换命令将UNIX System替换成UNIX Operating System

5)模式空间的内容变为”the UNIX Operating System”

6)全部编辑命令执行完毕,输出模式空间中的行

 

3. 地址匹配

地址匹配限制sed的编辑命令到底应用在哪些行上。默认状况下,sed是全局匹配的,即对全部输入行都应用指定的编辑命令,这是由于sed依次读入每一行,每一行都会成为当前行并被处理,因此s/apple/Pear/g会将全部输入行的apple替换成pear。

但若是经过指定地址范围,则只会替换范围内的行,譬如:

echo "Harry loves apple" | sed '/Mary/s/apple/pear/'
==> Harry loves apple

/Mary/是一个正则表达式,表示匹配包含Mary的行,所以”Harry loves apple”不会被替换.

sed命令中能够包含0个、1个或者2个地址(地址对)。地址能够为正则表达式(如/Mary/),行号或者特殊的行符号(如$表示最后一行):

  • 若是没有指定地址,默认将编辑命令应用到全部行
  • 若是指定一个地址,只将编辑命令应用到匹配该地址的行;
  • 若是指定一个地址对(addr1,addr2),则将编辑命令应用到地址对中的全部行(包括起始和结束);
  • 若是地址后面有一个感叹号(!),则将编辑命令应用到不匹配该地址的全部行

为了方便理解上述内容,咱们以删除命令(d)为例来讲明。

1)以行号指定地址

默认不指定地址将会删除全部行:

sed 'd' file

指定地址则删除匹配的行,如删除第一行:

sed '1d' file

或者删除最后一行,$符号在这里表示最后一行,这点要下正则表达式中的含义区别开来:

sed '$d' file

经过指定地址对能够删除该范围内的全部行,例如删除第3行到最后一行:

sed '2,$d' file

这里经过指定行号删除,行号是sed命令内部维护的一个计数变量,该变量只有一个,而且在多个文件输入的状况下也不会被重置。

 

2)经过正则表达式来指定地址

删除包含MA的行:

sed '/MA/d' file

删除从包含Alice的行开始到包含Hubert的行结束的全部行:

sed '/Alice/,/Hubert/d' file

固然,行号和地址对是能够混用的,删除第二行到包含Hubert的行结束的全部行:

sed '2,/Hubert/d' file

若是在地址后面指定感叹号(!),则会将命令应用到不匹配该地址的行:

sed '2,/Hubert/!d' file

以上介绍的都是最基本的地址匹配形式,GNU Sed基于此添加了几个扩展的形式,具体能够看man手册,或者能够看Sed 命令地址匹配问题总结

上面说的内容都是对匹配的地址执行单个命令,若是要执行多个编辑命令要怎么办?sed中能够用{}来组合命令,就比如编程语言中的语句块,例如:

sed -n '1,4{s/ MA/, Massachusetts/;s/ PA/, Pennsylvania/;p}' file

 

5、procedure

对于sed编辑命令的语法有两种约定,分别是

[address]procedure               # 第一种
[line-address]procedure          # 第二种

第一种[address]是指能够为任意地址包括地址对,第二种[line-address]是指只能为单个地址。

如下是要介绍的所有基础命令:

名称

命令

语法

说明

替换

s

[address]s/pattern/replacement/flags

替换匹配的内容

删除

d

[address]d

删除匹配的行

插入

i

[line-address]i\text

在匹配行的前方插入文本

追加

a

[line-address]a\text

在匹配行的后方插入文本

行替换

c

[address]c\text

将匹配的行替换成文本text

打印行

p

[address]p

打印在模式空间中的行

打印行号

=

[address]=

打印当前行行号

打印行

l

[address]l

打印在模式空间中的行,同时显示控制字符

转换字符

y

[address]y/SET1/SET2/

SET1中出现的字符替换成SET2中对应位置的字符

读取下一行

n

[address]n

将下一行的内容读取到模式空间

读文件

r

[line-address]r file

将指定的文件读取到匹配行以后

写文件

w

[address]w file

将匹配地址的全部行输出到指定的文件中

退出

q

[line-address]q

读取到匹配的行以后即退出

 

1. 替换命令: s

语法:

[address]s/pattern/replacement/flags

其中[address]是指地址,pattern是替换命令的匹配表达式,replacement则是对应的替换内容,flags是指替换的标志位,它能够包含如下一个或者多个值:

  • n: 一个数字(取值范围1-512),代表仅替换前n个被pattern匹配的内容;
  • g: 表示全局替换,替换全部被pattern匹配的内容;
  • p: 仅当行被pattern匹配时,打印模式空间的内容;
  • w file:仅当行被pattern匹配时,将模式空间的内容输出到文件file中;

 

若是flags为空,则默认替换第一次匹配,如

echo "column1 column2 column3 column4" | sed 's/ /;/'
==> column1;column2 column3 column4

若是flags中包含g,则表示全局匹配:

echo "column1 column2 column3 column4" | sed 's/ /;/g'
==> column1;column2;column3;column4

若是flags中明确指定替换第n次的匹配,例如n=2:

echo "column1 column2 column3 column4" | sed 's/ /;/2'
==> column1 column2;column3 column4

当替换命令的pattern与地址部分是同样的时候,好比/regexp/s/regexp/replacement/能够省略替换命令中的pattern部分,这在单个编辑命令的状况下没多大用处,可是在组合命令的场景下仍是能省很多功夫的。

 

譬如咱们有一个文件prince,里面的内容是:

If someone loves a flower, of which just one single blossom grows in all the 
millions and millions of stars, it is enough to make him happy just to look at 
the stars. He can say to himself, "Somewhere, my flower is there…" But if the 
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important

!

如今要在flower后面增长(“s”),同时在被修改的行前面增长+号,如下是使用的sed命令:

sed '/flower/{s//&("s")/;s/^/+ /}' prince

这里咱们用到了组合命令,而且地址匹配的部分和第一个替换命令的匹配部分是同样的,因此后者咱们省略了,在replacement部分用到了&这个元字符,它表明以前匹配的内容,这点咱们在后面介绍。执行后的结果为:

+

 If someone loves a flower

("s"),

 of which just one single blossom grows in all the
millions and millions of stars, it is enough to make him happy just to look at 

+

 the stars. He can say to himself, "Somewhere, my flower

("s")

 is there…" But if the 

+

 sheep eats the flower

("s")

, in one moment all his stars will be darkened… And you think that is not important!
替换命令的一个技巧是中间的分隔符是能够更改的,这个技巧在有些地方很是有用,好比路径替换,下面是采用默认的分隔符和使用感叹号做为分隔符的对比:
find /usr/local -maxdepth 2 -type d | sed 's//usr/local/man//usr/share/man/'
find /usr/local -maxdepth 2 -type d | sed 's!/usr/local/man!/usr/share/man!'

 

替换命令中还有一个很重要的部分——replacement(替换内容),即将匹配的部分替换成replacement。在replacemnt部分中也有几个特殊的元字符,它们分别是:

  • &: 被pattern匹配的内容;
  • \num: 被pattern匹配的第num个分组(正则表达式中的概念,\(..\)括起来的部分称为分组;
  • \: 转义符号,用来转义&,\, 回车等符号

2. 删除命令: d

语法:

[address]d

删除命令能够用于删除多行内容,例如1,3d会删除1到3行。删除命令会将模式空间中的内容所有删除,而且致使后续命令不会执行而且读入新行,由于当前模式空间的内容已经为空。咱们能够试试:

sed '2,${d;=}' prince 
==> If someone loves a flower, of which just one single blossom grows in all the

以上命令尝试在删除第2行到最后一行以后,打印当前行号(=),可是事实上并无执行该命令。

 

3. 插入行/追加行/替换行命令: i/a/c

语法:

# Append 追加
[line-address]a\text
# Insert 插入
[line-address]i\text
# Change 行替换 
[address]c\text

以上三个命令,行替换命令(c)容许地址为多个地址,其他两个都只容许单个地址(注:在ArchLinux上测试代表,追加和插入命令都容许多个地址,sed版本为GNU sed version 4.2.1

追加命令是指在匹配的行后面插入文本text;相反地,插入命令是指匹配的行前面插入文本text;最后,行替换命令会将匹配的行替换成文本text。文本text并无被添加到模式空间,而是直接输出到屏幕,所以后续的命令也不会应用到添加的文本上。注意,即便使用-n参数也没法抑制添加的文本的输出。

咱们用实际的例子来简单介绍下这三个命令的用法,用上面的文本prince:

如今,咱们要在第2行后面添加'------':

sed '2a\
-------------------------\
' prince

输出:

If someone loves a flower, of which just one single blossom grows in all the
millions and millions of stars, it is enough to make him happy just to look at
-------------------------

the stars. He can say to himself, "Somewhere, my flower is there…" But if the
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important!

或者能够在第3行以前插入:

sed '3i\
--------------------------\
 ' prince

输出:

If someone loves a flower, of which just one single blossom grows in all the
millions and millions of stars, it is enough to make him happy just to look at
--------------------------

the stars. He can say to himself, "Somewhere, my flower is there…" But if the
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important!

咱们来测试下文本是否确实没有添加到模式空间,由于模式空间中的内容默认是会打印到屏幕的:

sed -n '2a\
---------------------------\
' prince

输出:

---------------------------

经过-n参数来抑制输出后发现插入的内容依然被输出,因此能够断定插入的内容没有被添加到模式空间。

使用行替换命令将第2行到最后一行的内容所有替换成'----':

sed '2,$c\
---------------------------\
' prince

输出:

If someone loves a flower, of which just one single blossom grows in all the
---------------------------

 

4. 打印命令: p/l/=

这里纯粹的打印命令应该是指p,可是由于后二者(l和=)和p差很少,而且相对都比较简单,因此这里放到一块儿介绍。

[address]p
[address]=
[address]l

p命令用于打印模式空间的内容,例如打印文件的第一行:

sed -n '1p' prince
==> If someone loves a flower, of which just one single blossom grows in all the

l命令相似p命令,不过会显示控制字符,这个命令和vim的list命令类似,例如:

echo "column1 column2 column3^M" | sed -n 'l'
==> column1tcolumn2tcolumn3r$

=命令显示当前行行号,例如:

sed '=' prince
==>
1
If someone loves a flower, of which just one single blossom grows in all the 
2
millions and millions of stars, it is enough to make him happy just to look at 
3
the stars. He can say to himself, "Somewhere, my flower is there…" But if the 
4
sheep eats the flower, in one moment all his stars will be darkened… And you think that is not important!
5

 

5. 转换命令: y

转换命令的语法是:

[address]y/SET1/SET2/

它的做用是在匹配的行上,将SET1中出现的字符替换成SET2中对应位置的字符,例如1,3y/abc/xyz/会将1到3行中出现的a替换成x,b替换成y,c替换成z。是否是以为这个功能很熟悉,其实这一点和tr命令是同样的。能够经过y命令将小写字符替换成大写字符,不过命令比较长:

echo "hello, world" | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'
==> HELLO, WORLD

 

6. 取下一行命令: n

语法:

[address]n

n命令为将下一行的内容提早读入,而且将以前读入的行(在模式空间中的行)输出到屏幕,而后后续的命令会应用到新读入的行上。所以n命令也会同d命令同样改变sed的控制流程。

cat text

==>

.H1 "On Egypt"

Napoleon, pointing to the Pyramids, said to his troops:

"Soldiers, forty centuries have their eyes upon you."

如今要将.H1后面的空行删除:

sed '/.H1/{n;/^$/d}' text
==>
.H1 "On Egypt"
Napoleon, pointing to the Pyramids, said to his troops:
"Soldiers, forty centuries have their eyes upon you."

 

7.  读写文件命令: r/w

语法是:

[line-address]r file
[address]w file

读命令将指定的文件读取到匹配行以后,而且输出到屏幕,这点相似追加命令(a)。咱们以书中的例子来说解读文件命令。假设有一个文件text:

cat text
==> 
For service, contact any of the following companies:
[Company-list]
Thank you.

同时咱们有一个包含公司名称列表的文件company.list:

cat company.list 
==>
Allied
Mayflower
United

如今咱们要将company.list的内容读取到[Company-list]以后:

sed '/^[Company-list]/r company.list' text
=>
For service, contact any of the following companies:
[Company-list]
Allied
Mayflower
United
Thank you.

写命令将匹配地址的全部行输出到指定的文件中。假设有一个文件内容以下,前半部分是人名,后半部分是区域名称:

cat text 
==>
Adams, Henrietta Northeast
Banks, Freda South
Dennis, Jim Midwest
Garvey, Bill Northeast
Jeffries, Jane West
Madison, Sylvia Midwest
Sommes, Tom South

如今咱们要将不一样区域的人名字写到不一样的文件中:

sed '/Northeast$/w region.northeast
/South$/w region.south
/Midwest$/w region.midwest
/West$/w region.west' text
==>
Adams, Henrietta Northeast
Banks, Freda South
Dennis, Jim Midwest
Garvey, Bill Northeast
Jeffries, Jane West
Madison, Sylvia Midwest
Sommes, Tom South

 

8. 退出命令: q

语法:

[line-address]q

当sed读取到匹配的行以后即退出,不会再读入新的行,而且将当前模式空间的内容输出到屏幕。例如打印前3行内容:

sed '3q' prince
==>
If someone loves a flower, of which just one single blossom grows in all the 
millions and millions of stars, it is enough to make him happy just to look at 
the stars. He can say to himself, "Somewhere, my flower is there…" But if the

打印前3行也能够用p命令:

sed -n '3p' prince

可是对于大文件来讲,前者比后者效率更高,由于前者读取到第N行以后就退出了。后者虽然打印了前N行,可是后续的行仍是要继续读入,只不会不做处理。

到此为止,sed基础命令的部分就介绍完了。

 

6、小结

Linux系统工具众多,功能也互相重复,这些重复部分的语法还各不相同,好比 grep awk sed 都有正则表达式匹配的功能,可是三者的正则表达式语法就不相同。每一个工具还分 GNU 版和不是 GNU 版,之间的差异也很大,即便都是 GNU 版,那么版本号的细微差异也会带来不少差异。

在普通的行处理任务方面,用sed很好,由于命令很简洁。

 

7、参考

SED入门

Sed and awk 笔记之 sed 篇:简单介绍

awk&sed!!!

(完)

相关文章
相关标签/搜索