sed是streameditor的简写,英文称之为“流编辑器”。

sed命令是一个面向行处理的工具,它以“行”为处理单位,针对每一行进行处理,处理后的结果会输出到标准输出(STDOUT)。你会发觉sed命令是很懂礼貌的一个命令,它不会对读取的文件做任何轻率的更改,而是将内容都输出到标准输出中。

我们来瞧瞧sed的命令格式:

sed command file

sed的工作原理是哪些昨晚我们说了,sed命令是面向“行”进行处理的,每一次处理一行内容。处理时,sed会把要处理的行存贮在缓冲区中,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直至文件末尾。这个缓冲区被称为“模式空间”(patternspace)。

如上面所说linux命令passwd,在这个处理过程中,sed命令并不会对文件本身进行任何修改。

我们来一起看一个最最简单的sed命令的事例,让你们对它有个感性的认识。

#我们先来看看原文件的内容
[roc@roclinux ~]$ cat roc.txt
test 1
test2
testtest
XtestX
BBtest
 
#我们想用sed命令来删除文件中带字符“2”的行
[roc@roclinux ~]$ sed '/2/d' roc.txt
test 1
testtest
XtestX
BBtest

此反例是借助sed来删掉roc.txt文件中富含字符“2”的行。见到了吧,事例很简单,这个命令的command部份是/2/d,并且它是用单冒号括上去的。你也一定要学着这样做,用到sed,别忘了用单冒号将command部份括上去。

/2/d中的d表示删掉,意思是说,只要某行内容中富含字符2,就删除这一行。(sed所谓的删掉都是在模式空间中执行的,不会真正改动roc.txt原文件。)想用sed命令实现cut命令的疗效如果我们想实现类似于cut-d:-f1/etc/passwd的疗效,也就是以逗号为间隔符提取第1个域,用sed命令应当如何操作呢?

#先来一起看看/etc/passwd文件的内容
[roc@roclinux ~]$ head -n 5 /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
 
#我们这回用sed命令来提取文件中每行的第一个域, 间隔符是冒号
[roc@roclinux ~]$ head -n 5 /etc/passwd|sed 's/:.*$//'
root
bin
daemon
adm
lp

见到了吧,我们将command部份指定成了's/:.*$//',表示我们要把每一行的第一个逗号到结尾的部份都清空,这样留下的便是第一个逗号前的内容啦。sed都有什么好用的选项说到sed命令的选项,就不得不提-n选项,想把这个选项介绍清楚,还是要费一些头脑和笔端的。

后面提及,sed会将模式空间里的行经过处理后输出到标准输出,这是默认的处理方法。也就是说,除非你使用“d”来删掉此行,否则经过“模式空间”处理的行都是会被输出到标准输出(屏幕)上的。我们一上去看下边的事例:

#还是先来看看原文件的内容
[roc@roclinux ~]$ cat roc.txt
1
2
3
4
5
 
#仔细看, 输出中出现了两个“4”
[roc@roclinux ~]$ sed ‘/4/p’ roc.txt
1
2
3
4
4
5

看,所有的原始文件内容都被输出来了,但是富含字符4的行被输出了两遍。

但这就是sed命令的工作原理,它会不问青红皂白地把经过处理的行先输出下来,之后再执行前面的动作。(在这儿我们设定了p,表示复印此行。)这显著不符合我们的本意,我们只是想让sed命令找到富含4的行再输出。

这时侯,不妨加上-n选项试一试,你会发觉,结果显得如你所愿了。

[roc@roclinux ~]$ sed -n '/4/p' roc.txt
4

-n选项会很严肃地警告sed命令:除非是明晰表明要输出的行,否则不要给我胡乱输出。-n选项时常和p配合使用,其涵义就是,输出这些匹配的行。command部份花样好多还记得我们在后面介绍过的,sed命令的命令格式是这样的:

$ sed command file

其中,command部份是sed命令的真谛,对command部份的把握程度决定了你是不是sed大神。

command部份可以分为两块知识:一块是范围设定,一块是动作处理。

范围设定,可以采用两种不同的形式来抒发:指定行数:例如‘3,5’表示第3、第4和第5行;而‘5,$’表示第5行至文件最后一行。模式匹配:例如/^[^dD]/表示匹配行首不是以d或D开头的行。

而动作处理部份,会提供很丰富的动作供你选择,下边就来介绍几个最常用的动作吧:展示sed实力的时侯到了事实胜过诡辩,我们准备通过四个反例,让你们感遭到sed命令真的是一位“实力派”选手。

第一个反例,我们来显示test文件的第10行到第20行的内容:

#我们采用了刚才提到的指定行区间的方法
[roc@roclinux ~]$ sed -n '10,20p' test

linuxpasswd命令_linux命令passwd_linux中passwd命令

第二个反例,我们尝试将所有以d或D开头的行里的所有大写x字符变为小写X字符:

[roc@roclinux ~]$ sed '/^[dD]/s/x/X/g' test

这个用法值得一讲,我们在command部份采用了/AA/s/BB/CC/g的句型格式,这表示我们要匹配到文件中带有AA的行,但是将这种行中所有的BB替换成CC。

第三个事例,我们要删掉每行最后的两个字符:

#点号表示一个单个字符, 两个点号就表示两个单个字符
[roc@roclinux ~]$ sed 's/..$//' test

有人可能会问,用sed‘/..$/d’test为何不行呢,d不是表示删掉么?用d是不行的,这是由于d表示删掉整行内容,而非字符。'/..$/d'表示的是匹配所有末尾富含两个字符的行,之后删掉这一整行内容,似乎这和我们的本意是偏颇的。

第四个反例,我们希望删掉每一行的前两个字符:

[roc@roclinux ~]$ sed 's/..//' test

通过这四个反例,相信你们对sed命令的最常见用法早已有了很直观的认识了,不妨在自己的实际工作中把这种知识用上去吧。&符号的妙用我们依然通过一个场景来讲解这个知识点。

#按照惯例, 先展示文件的内容
[roc@roclinux ~]$ cat mysed.txt
Beijing
London
 
#我们使用到了&符号, 大家试着猜一猜它的作用
[roc@roclinux ~]$ sed 's/B.*/&2008/' mysed.txt
Beijing2008
London

不卖关子linux版qq,答案出炉啦,这个命令的作用是将包含‘B.*’的字符串前面加上2008四个字符。这个命令中我们用到了&字符,在sed命令中,它表示的是“之前被匹配的部份”,在我们的事例中其实就是Beijing啦!

我们再通过一个反例加强一下你们对&符号的理解:

#这个例子或许更易理解
[roc@roclinux 20160229]$ sed 's/Bei/&2008/' mysed.txt
Bei2008jing
London

sed中的括弧有深意在sed命令中,虽然小括弧‘()’也是有深意的。我们开门见山,通过一个反例,让你们见识一下小括弧的威力:

[roc@roclinux ~]$ echo "hello world" | sed 's/(hello).*/world 1/'
world hello

我们看见,原先是“helloworld”,经过sed的处理,输出弄成了“worldhello”。

这个反例中就用到了小括弧的知识,我们称之为“sed的预储存技术”,也就是命令中被“(”和“)”括上去的内容会被依次暂存上去,储存到1、2…里面。这样你就可以使用‘N’形式来调用那些预储存的内容了。

来继续看一个反例,我们希望只在每行的第一个和最后一个Beijing前面加上2008字符串,言下之意就是,不仅每行的第一个和最后一个2008之外,这一行中间出现的Beijing旁边就不要加2008啦。这个需求,真的是很复杂很个性化,但sed命令始终可以挺好地满足:

#先看下文件内容, 第一行中出现了4个Beijing
[roc@roclinux ~]$ cat mysed.txt
Beijing Beijing Beijing Beijing
London London London London
 
#效果实现啦, 可是, 命令真的好复杂
[roc@roclinux ~]$ sed 's/(Beijing)(.*)(Beijing)/12008232008/' mysed.txt
Beijing2008 Beijing Beijing Beijing2008
London London London London

这个命令确实足够复杂,用流行的语言说就是“足够催泪”。这个反例中我们再度使用了预储存技术,储存了三项内容,分别代表第一个Beijing、中间的内容、最后的Beijing。而针对1和3,我们在其前面追加了2008这个字符串。更聪明的定位行范围实践是学习知识最好的方式,相信你们看了这个反例后,就明白怎么更好地定位行范围了:

#文件内容展示一下
[roc@roclinux ~]$ cat mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
Beijing 2007
 
#我们想展示匹配了2005的行和2007的行之间的内容
[roc@roclinux ~]$ sed -n ‘/2005/,/2007/p’ mysed.txt
Beijing 2005
Beijing 2006
Beijing 2007

我们使用/2005/来匹配行范围的首行,用/2008/来匹配行范围的尾行。可以见到,在匹配尾行时,只要碰到第一个符合要求的行,才会停止,而不会再继续向后匹配了。所以,sed命令只是匹配到了第一个2007,并没有匹配到第二个2007。用-e选项来设置多个command还记得command部份吧,现今有一个好消息要告诉你,那就是sed命令可以包含不只一个command。假如要包含多个command,只需在每位command上面分别加上一个-e选项即可。

#我们通过2个-e选项设置了两个command
[roc@roclinux ~]$ sed -n -e ‘1,2p’ -e ‘4p’ mysed.txt
Beijing 2003
Beijing 2004
Beijing 2006

有一个地方值得你们注意,那就是-e选项的前面要立刻接command内容,不容许再参杂其他选项。

-e选项支持设置多个command,这本来是一件好事情,让我们可以更便捷地实现一些替换疗效。并且,这也给我们带来了幸福的苦恼,如果我们设定了好多个command,那它们的执行次序是如何的呢?

倘若这一点不搞明白,-e选项带来的其实只有混乱而非方便。我们来一起瞧瞧下边的事例:

#先看看文件的内容
[roc@roclinux ~]$ cat mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
 
#我们设置了两个command
[roc@roclinux ~]$ sed -e ‘s/Beijing/London/g’ -e ‘/Beijing/d’ mysed.txt
London 2003
London 2004
London 2005
London 2006
London 2007
London 2008

前一个command表示将Beijing替换为London,而后一个command表示要删掉包含了Beijing字符串的行,然而最后的结果却是输出了所有行,并没有发觉被删掉的行。这是由于第一个command早已将Beijing都替换成了London,所以怪第二个command找不到Beijing了。

我们再来把里面事例中的command颠倒一下位置,瞧瞧疗效怎么:

#我们先指定删除动作, 再指定替换动作
[roc@roclinux 20160229]$ sed -e '/Beijing/d' -e 's/Beijing/London/g' mysed.txt
[roc@roclinux 20160229]$

通过这两个小反例雨林木风linux,我们可以很清晰地看见,多个command之间,是根据在命令中的先后次序来执行的。用-f选项设定command文件假如你的sed命令的command部份很长,这么可以将内容讲到一个单独的文件中,之后使用-f选项来指定这个文件作为我们sed命令的command部份:

#这是我们事先写好的文件
[roc@roclinux ~]$ cat callsed
/2004/,/2006/p
 
#我们用-f选项来指定command文件
[roc@roclinux ~]$ sed -n -f callsed mysed.txt
Beijing 2004
Beijing 2005
Beijing 2006

挺好理解吧,-f选项并不难,但是我会常常使用,由于一些比较常用的匹配规则,我就会存到单独的文件中,不用再费头脑记忆啦。内容插入sed命令远比你想像的要强悍,它除了可以处理本行内容,还可以在这一行的前面插入内容:

#我们将要插入的内容保存到一个单独的文件中
[roc@roclinux ~]$ cat ins.txt
====China====
 
#展示一下我们要处理的文件
[roc@roclinux ~]$ cat mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
 
#看, 我们使用r来实现插入
[roc@roclinux ~]$ sed ‘/2005/r ins.txt’ mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
====China====
Beijing 2006
Beijing 2007
Beijing 2008

通过疗效可以看下来,我们在文件中的富含2005字符串的行的下边一行插入了ins.txt文件的内容。

不仅可以通过指定文件来插入外,似乎还可以使用a在特定行的“下面”插入特定内容:

#文件内容
[roc@roclinux ~]$ cat new.txt
Beijing 2004
Beijing 2005
Beijing 2006
 
#我们希望在2004的下一行插入China
[roc@roclinux ~]$ sed ‘/2004/aChina’ mysed.txt
Beijing 2003
Beijing 2004
China
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008

可以看见,我们只要使用a之后加上要插入的内容就可以轻松实现啦。

有些同学会问,既然可以在一行的下边插入内容,那是否可以在一行的前面插入内容呢?答案是其实可以,那就是使用i动作:

[roc@roclinux ~]$ sed ‘/2004/iChina’ mysed.txt
Beijing 2003
China
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008 

说一说y动作在介绍y动作之前,我们先来瞧瞧它可以实现哪些疗效:

#原文件内容
[roc@roclinux ~]$ cat mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
 
#y就是按照字符顺序, 实现前后的替换
[roc@roclinux 20160229]$ sed 'y/ei/ie/' mysed.txt
Biejeng 2003
Biejeng 2004
Biejeng 2005
Biejeng 2006
Biejeng 2007
Biejeng 2008

这个反例似乎早已很清楚了,我们希望将所有的e和i互换。

有些同学会问,y///和s///有哪些区别呢?主要有以下两点:

这时,一些GEEK似乎会想到一种情况,那就是y/ee/ei/会形成哪些疗效呢?由于这儿面出现了两个同样的字符,我们还是通过事例来看一下:

[roc@roclinux 20160229]$ sed 'y/ee/ie/' mysed.txt
Biijing 2003
Biijing 2004
Biijing 2005
Biijing 2006
Biijing 2007
Biijing 2008

见到了吧,假如source部份出现了重复的字符,则只有第一次出现的对位替换会形成疗效,前面的并不会起作用。其实下边这个反例愈发清晰些:

#原文件内容
[roc@roclinux ~]$ cat mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
 
#iji到iba的替换中, 只有j到b起到了效果
[roc@roclinux 20160229]$ sed 'y/iji/iba/' mysed.txt
Beibing 2003
Beibing 2004
Beibing 2005
Beibing 2006
Beibing 2007
Beibing 2008

通过n动作来控制行的下移有时我们希望实现隔行处理的疗效,例如只需对质数行做某个替换,这时侯,我们就须要n动作的帮忙啦:

#原文件内容
[roc@roclinux ~]$ cat mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
 
#我们同时使用了n动作和y动作
[roc@roclinux ~]$ sed ‘/200/{n;y/eijng/EIJNG/;}’ mysed.txt
Beijing 2003
BEIJING 2004
Beijing 2005
BEIJING 2006
Beijing 2007
BEIJING 2008

你会发觉,小写的BEIJING是隔行出现的。这就是n选项在起作用,它的真实作用是将下一行内容放在处理缓存中,这样,就让当前这一行躲避开了替换动作,是不是有点像小时候玩游戏时通过左右键躲避开BOSS的大招,哈哈。将指定行写入到特定文件中文章要步入尾声了,我们最后再教你们一个特别实用的动作,那就是w动作linux命令passwd,它可以将匹配到的内容写入到另一个文件中,即拿来实现内容的筛选与保存:

#将包含2004、2005、2006的行保存到new.txt文件中
[roc@roclinux ~]$ sed ‘/200[4-6]/w new.txt’ mysed.txt
Beijing 2003
Beijing 2004
Beijing 2005
Beijing 2006
Beijing 2007
Beijing 2008
 
#我们要的内容已经乖乖到碗里来了
[roc@roclinux ~]$ cat new.txt
Beijing 2004
Beijing 2005
Beijing 2006

好了,sed的流艺术系列到这儿就全部结束啦,相信你对sed早已有了初步的认识,可以在实战中露双手了。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复