柴少鹏的官方网站 技术在分享中进步,水平在学习中升华

sed命令详解

     sed是stream editor的简称,也就是流编辑器。它一次处理一行内容,处理时,把当前处理的行存储在临时缓存区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有 改变,除非你使用重定向存储输出。

     sed,awk和grep号称shell编程三剑客,也是作为运维人员必须要掌握的,最好掌握的深入一点,这样在我们进行文本处理的或者编写脚本的时候会有很大的好处。工作中也是经常用到的,所以记录一下。


一、使用语法

1.1 sed命令的使用规则:

sed [options] 'command' file(s) 

sed [options] -f scriptfile file(s)

1.2 OPTION为可选的,常用的option:

-n : 使用安静(silent)模式。在一般sed的用法中,所有来自stdin的内容一般都会被列出到屏幕上。但如果加上-n参数后,则只有经过sed特殊处理的那一行(或者动作)才会被列出来;

-e : 直接在指令列模式上进行 sed 的动作编辑

-f : 直接将 sed 的动作写在一个文件内, -f filename 则可以执行filename内的sed命令

-r : 让sed命令支持扩展的正则表达式(默认是基础正则表达式)

-i :  直接修改读取的文件内容,而不是由屏幕输出。


1.3 sed的command命令:

a\ :在当前行下面插入文本。 

i\  :在当前行上面插入文本。 

c\ :把选定的行改为新的文本。 

d  :删除,删除选择的行。 

D  :删除模板块的第一行。 

s  :替换指定字符 

h :拷贝模板块的内容到内存中的缓冲区。 

H :追加模板块的内容到内存中的缓冲区。 

g  :获得内存缓冲区的内容,并替代当前模板块中的文本。 

G  :获得内存缓冲区的内容,并追加到当前模板块文本的后面。 

l  :列表不能打印字符的清单。 

n  :读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。 

N  :追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。 

p  :打印模板块的行。

P :(大写) 打印模板块的第一行。 

q  :退出Sed。 

b  :lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。 

r file : 从file中读行。 

t label : if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。 

T label : 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。 

w file : 写并追加模板块到file末尾。 

W file : 写并追加模板块的第一行到file末尾。 

! : 表示后面的命令对所有没有被选定的行发生作用。 

= :打印当前行号码。 

# :把注释扩展到下一个换行符以前。


1.4 sed替换标记

g :表示行内全面替换。 

p :表示打印行。 

w :表示把行写入一个文件。

x  :表示互换模板块中的文本和缓冲区中的文本。 

y  :表示把一个字符翻译为另外的字符(但是不用于正则表达式) 

\1 :子串匹配标记 

&  :已匹配字符串标记

1.5 sed元字符集

^  :匹配行开始,如:/^sed/匹配所有以sed开头的行。

$  :匹配行结束,如:/sed$/匹配所有以sed结尾的行。

.  :匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d。

*  :匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。

[]  :匹配一个指定范围内的字符,如/[az]ed/匹配aed和zed。

[^] :匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。

\(..\) :匹配子串,保存匹配的字符,如s/\(love\)able/\1rs,loveable被替换成lovers。

&  :保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。

\<  :匹配单词的开始,如:/\ 匹配单词的结束,如/love\>/匹配包含以love结尾的单词的行。

x\{m\}  :重复字符x,m次,如:/0\{3\}/匹配包含3个0的行。

x\{m,\} :重复字符x,至少m次,如:/0\{2,\}/匹配至少有2个0的行。

x\{m,n\} :重复字符x,至少m次,不多于n次,如:/0\{1,3\}/匹配1~3个0的行。

[:digit:] : 所有数字,  相当于0-9

[:lower:] :所有的小写字母

[:upper:]  :所有的大写字母

[:alpha:]  :所有的字母

[:alinum:] :可打印的字符(包括空白字符)

[:space:] :空白字符

[:punct:] :标点符号字符

[:blank:] :空格和制表符

[:cntrl:] :控制字符

[:graph:] :可打印的和可见的(非空格)字符

[:print:] :可打印的字符(包括空白字符)

[:xdigit:] : 十六进数字

另外:\n 表示新的一行, \r 表示回车, \t 表示水平制表符, \v 表示垂直制表符, \b 表示后退符, \a 表示"alert", \0xx 表示转换为八进制的ASCII码。


1.6 指定命令行上的多重指令

第一种:用分好分割指令

第二种:在每个指令前放置-e

第三种:使用Bourne shell的分行指令功能。再输入单引号后按回车Enter键,就会出现多行输入的提示符(>)。 


博文来自:www.51niux.com

二、实例演示

2.1 显示操作(n和p来结合打印输出)

sed -n '1p' file      #显示第一行
sed -n '$p' file    #显示最后一行
sed -n '2,5p' file  #显示第二行到第五行
sed -n '6,$p' file  #显示第六行到最后一行

# nl 1.txt |sed -n 'p;n'    #打印奇数行,同sed -n '1~2p'输出一致

     1 list  listen321  list123

     3 listen  list222  listen123

# nl 1.txt |sed -n 'n;p'  #打印偶数行,同sed -n '2~2p'输出一致

     2 listn  listen111  list123

     4 listen  listen333  listen123

# sed '10q' /etc/passwd #q是退出的意思,这里就是打印完10行后退出。一般我们都是过滤小文件体会不出来,如果去过滤大文件的时候,不用遍历全部节省时间。这个q也经常会用在sed的脚本里面使用。

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

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin

sed -n '/00/p' /root/install.log  #将/root/install.log 中包含00的行打印出来

sed -n '/1\.2/p' /root/install.log #将/root/install.log 中包含1.2的行打印出来,因为.代表0或1个,所以这里需要\转义。

blob.png

# sed -n '/^#/!p' /etc/vsftpd/vsftpd.conf  #打印文件非#开头的行,就是先取出以#号开头的行,再用!取反

anonymous_enable=YES

local_enable=YES

write_enable=YES

local_umask=022

dirmessage_enable=YES

xferlog_enable=YES

connect_from_port_20=YES

xferlog_std_format=YES

listen=YES


pam_service_name=vsftpd

userlist_enable=YES

tcp_wrappers=YES

# sed -n '/^#/!{/^$/!p}' /etc/vsftpd/vsftpd.conf  #对取出的结果再用{/^$/!p}再精准匹配,再将空格去除掉,从结果看,除了没有空格结果跟上方一致。

anonymous_enable=YES

local_enable=YES

write_enable=YES

local_umask=022

dirmessage_enable=YES

xferlog_enable=YES

connect_from_port_20=YES

xferlog_std_format=YES

listen=YES

pam_service_name=vsftpd

userlist_enable=YES

tcp_wrappers=YES

# sed -e '/^#/d' -e '/^$/d' /etc/vsftpd/vsftpd.conf   #跟上方的效果是一样的。

注:下面三个演示一方面是为了显示行号,主要是为了展示多重指令的操作。

# sed -n  '/Virtual Drive: 0/='  MegaSAS.log  #打印现在在MegaSAS.log文件中,字符串Virtual Drive: 0都在文件的第几行出现了。

3

143

283

423

563

# sed -n -e '/Virtual Drive: 0/p' -e  '/Virtual Drive: 0/='  MegaSAS.log  #再复杂一点,就是用-e来指定多重指令,不仅把内容显示出来,还把行号打印出来。

Virtual Drive: 0 (Target Id: 0)

3

Virtual Drive: 0 (Target Id: 0)

143

Virtual Drive: 0 (Target Id: 0)

283

Virtual Drive: 0 (Target Id: 0)

423

Virtual Drive: 0 (Target Id: 0)

563

# sed -n  '/Virtual Drive: 0/p;/Virtual Drive: 0/='  MegaSAS.log  #跟上面的效果一致,分号也可以用来做多重指令操作

Virtual Drive: 0 (Target Id: 0)

3

Virtual Drive: 0 (Target Id: 0)

143

Virtual Drive: 0 (Target Id: 0)

283

Virtual Drive: 0 (Target Id: 0)

423

Virtual Drive: 0 (Target Id: 0)

563

# sed -n ' 

> /Virtual Drive: 0/p

> /Virtual Drive: 0/=' MegaSAS.log

Virtual Drive: 0 (Target Id: 0)

3

Virtual Drive: 0 (Target Id: 0)

143

Virtual Drive: 0 (Target Id: 0)

283

Virtual Drive: 0 (Target Id: 0)

423

Virtual Drive: 0 (Target Id: 0)

563

# nl MegaSAS.log|sed -n '/Virtual Drive: 0/p'  #当然也可以借助nl来显示行号,这就涉及到跟其他命令组合使用了。

     2 Virtual Drive: 0 (Target Id: 0)

   130 Virtual Drive: 0 (Target Id: 0)

   258 Virtual Drive: 0 (Target Id: 0)

   386 Virtual Drive: 0 (Target Id: 0)

   514 Virtual Drive: 0 (Target Id: 0)


2.2 删除某行或多行(如果不加-i文件是不会发送改变的,只是会把你操作完的结果显示在屏幕上,你可以通过>指向新文件的方式将更改之后的内容保存下来,用新文件来替换旧文件)

#sed '1d' MegaSAS.log   #删除第一行

# sed '4,$d' MegaSAS.log  #删除第四行到结尾的行


Adapter 0 -- Virtual Drive Information:

Virtual Drive: 0 (Target Id: 0)

# sed '/Virtual Drive: 0/d' MegaSAS.log #删除带条件Virtual Drive: 0的行

# sed '/^$/d' /root/MegaSAS.log  #这里是使用了正则表达式,删除了空行

# sed '/[[:digit:]]/d' /root/MegaSAS.log   #这是将所有行中带数字的行删除掉


2.3 增加一行或多行字符串

# sed '1a sed test OK!+1.6' 2.txt   #1a表示在第一行的下一行增加字符串,字符串为 sed test OK!+1.6

abc 123 hah

sed test OK!+1.6

# sed '1a sed test OK\!\+1\.6 \tor is OK! \nhaha is OK!' 2.txt  #这里用\屏蔽了特殊含义,但是\t为水平制表符我们又跟着插入了一个nor is OK!,再插入一个\n换行符,再插入字符串haha is OK!。这样可以插入多行的内容。

abc 123 hah

sed test OK!+1.6         or is OK! 

haha is OK!

# sed '1,3a sed test OK\!\+1\.6 \tor is OK! \nhaha is OK!' 2.txt   #这就是多行插入,1,3行都在下一行插入内容。

abc 123 hah

sed test OK!+1.6        or is OK! 

haha is OK!

zouqi 321 das

sed test OK!+1.6       or is OK! 

haha is OK!

dasda 32131 321321

sed test OK!+1.6      or is OK! 

haha is OK!

21321  312321  31315adada

# sed '1,3i sed test OK\!\+1\.6 \tor is OK! \nhaha is OK!' 2.txt  #i就是在选定行的上面插入内容,这里就是再1-3行每行的上面插入我们要插入的内容

sed test OK!+1.6 or is OK! 

haha is OK!

abc 123 hah

sed test OK!+1.6 or is OK! 

haha is OK!

zouqi 321 das

sed test OK!+1.6 or is OK! 

haha is OK!

dasda 32131 321321


2.4 对整行做替换

# sed '1c #zhushi' 2.txt   #将2.txt的第一行替换成#zhushi

# sed '1,4c \#zhushi' 2.txt  #将2.txt的第一行到第四行换成一行的#zhushi,就相当于将1到4行删除了换成了一行的#zhushi

#zhushi

dadas  dada   das da da d

dad12321  31231  32132131


2.5 对文件内容进行查找并替换,这也是我们经常用到的一种方式,一般会加上参数-i,不过在操作之前要将文件替换掉,线上操作前要在线上做好测试。

# sed '1,3y/abcedfghi/ABCDEFGHI/' 1.txt  #用y来变形。把1-3行内所有的abcdefghi转换为大写,注意正则表达式元字符不能使用这个命令。从结果中可以看到第四行没有发生变化。当然也可以不指定行,就是全局替换。

lIst  lIstDn321  lIst123

lIstn  lIstDn111  lIst123

lIstDn  lIst222  lIstDn123

listen  listen333  listen123

# sed '/listen1/s@^@#@g' /root/1.txt  #在模糊匹配带有listen1的行的前面加上#。

list  listen321  list123

#listn  listen111  list123

#listen  list222  listen123

#listen  listen333  listen123

# sed '/listen1/s@$@ haha@g' /root/1.txt  #相反的就是再匹配条件的行尾加字段,与# sed '/listen1/s@\(.*\)@\1 haha@g' /root/1.txt 效果一致

list  listen321  list123

listn  listen111  list123 haha

listen  list222  listen123 haha

listen  listen333  listen123 haha

# sed -n 's/list/#list/p' 1.txt      #s表示替换指定字符,  s/list/#list/p :表示将包含list的行替换成前面带#号,p就是打印出来,文件名称是1.txt       

#list  listen321  list123

#listn  listen111  list123

#listen  list222  listen123

#listen  listen333  listen123

# sed -n 's/list/#list/gp' 1.txt    #这句话多了一个g,全面替换每一行,不加g只会将每一行第一个出现的替换掉,加了g一行中有几个都会被替换掉

#list  #listen321  #list123

#listn  #listen111  #list123

#listen  #list222  #listen123

#listen  #listen333  #listen123

# sed -n 's/list/#list/2gp' 1.txt   #g前面还可以加数字,表示从第几个匹配开始替换,我们这里是数字2就是从第二个匹配开始替换,你会发现第一个匹配没有被替换

list  #listen321  #list123

listn  #listen111  #list123

listen  #list222  #listen123

listen  #listen333  #listen123

# sed -n 's/\<list\>/#list/gp' 1.txt  #如果我只想对list做精准替换,而不是模糊的匹配替换呢,就需要用到\<以什么开头,\>以什么结尾,\<list\>这就是以list开头和结尾。

#list  listen321  list123

# sed  's/\<list[0-9]\{0,\}\>/#list/g' 1.txt  #如果我想对list包括list后面带数字的行做替换呢,就要用到正则表达式[0-9]表示所有的数字,\{0,\}表示数字至少出现0次或者以上。

#list  listen321  #list

listn  listen111  #list

listen  #list  listen123

listen  listen333  listen123

# sed  's/\<list[[:digit:] ]\{0,\}\>/#list/g' 1.txt #这个跟上面的效果是一致的。


定界符:

# sed -n 's/\/bin\/bash/\/sbin\/nologin/gp' /etc/passwd #这个例子主要是展示了定界符,也就是我们前面出现的:s/list/#list/中的///和现在出现的\\\,如果定界符出现在了样式内部需要进行转义,定界符还有###,@@@,:::,|||等等这种三个符号一起的。

root:x:0:0:root:/root:/sbin/nologin


已匹配字符串标记&:

# sed 's/\w\+/[&]/g' /etc/passwd  #正则表达式\w\+匹配每一个单词,使用[&]替换它也可以是#&等等,&对应前面匹配的每一个单词。# cat /etc/passwd |sed 's/\w\+/[&]/g' 效果一致

[root]:[x]:[0]:[0]:[root]:/[root]:/[bin]/[bash]

[bin]:[x]:[1]:[1]:[bin]:/[bin]:/[sbin]/[nologin]

# sed  's/listen1/**&**/g' 1.txt   #&已匹配的字符串标记,这就是将listen1前后都加上**

list  listen321  list123

listn  **listen1**11  list123

listen  list222  **listen1**23

listen  listen333  **listen1**23


子串匹配标记\n:

# echo aaa AAA | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'  #\(..\)用于匹配子串,所以aaa就被匹配为第一个子串也就是标记为\1,AAA就是第二个子串\,

AAA aaa

# sed -n 's#\(listen[0-9]\)#@\1#gp' /root/1.txt   #\(listen[0-9]\) 就是listen后面带数字的为匹配第一个子串,标记为\1

list  @listen321  list123

listn  @listen111  list123

listen  list222  @listen123

listen  @listen333  @listen123


2.6 指定行数做替换

# nl /etc/passwd|sed -n '/root/p'

     1 root:x:0:0:root:/root:/bin/bash

    11 operator:x:11:0:operator:/root:/sbin/nologin

# sed -n '1,10s/root/kaixin/gp' /etc/passwd

kaixin:x:0:0:kaixin:/kaixin:/bin/bash

# sed -n '1,20s/root/kaixin/gp' /etc/passwd

kaixin:x:0:0:kaixin:/kaixin:/bin/bash

operator:x:11:0:operator:/kaixin:/sbin/nologin

# sed '/list222/{n;s/listen/wokao/;}' 1.txt  #n命令,是下一行的意思。这句话的意思是如果list222被匹配到了,然后就n;跳转到此行的下一行,将listen替换为wokao。

list  listen321  list123

listn  listen111  list123

listen  list222  listen123

wokao  listen333  listen123


2.7 文件的读入和写出

# sed -i "/list/r  /etc/passwd" 1.txt  #将/etc/passwd的内容写入到1.txt文件中list关键字所在的行的下面。

# sed  's/listen/woshinige/gw 2.txt' 1.txt   #这是将1.txt文件中所有包括listen的更改为woshinige,并将更改的的内容的行插入到新的文件2.txt中

# sed  's/listen1/woshinige/gw 2.txt' 1.txt  #我们来验证一下,将listen1的更改为woshinige

# cat 2.txt   #经查看只是将更改的内容替换过来了而已。

listn  woshinige11  list123

listen  list222  woshinige23

listen  listen333  woshinige23


2.8 编写sed脚本

# cat  1.txt   #先查看一下我们测试文件的内容

list  listen321  list123

listn  listen111  list123

listen  list222  listen123

listen  listen333  listen123

# cat  1sed.sh   #查看一下我们简单小脚本的内容,就是把''里面的内容写到脚本里面,多个操作就写多行

#!/bin/bash

s/listen/wokaka/g

s/list/zoi|qi/g

# sed -f  1sed.sh  1.txt  #格式为:sed -f 脚本文件  要被匹配的文件

zoi|qi  wokaka321  zoi|qi123

zoi|qin  wokaka111  zoi|qi123

wokaka  zoi|qi222  wokaka123

wokaka  wokaka333  wokaka123

# nl 1.txt |sed -f  1sed.sh    #混合使用也可以

     1 zoi|qi  wokaka321  zoi|qi123

     2 zoi|qin  wokaka111  zoi|qi123

     3 wokaka  zoi|qi222  wokaka123

     4 wokaka  wokaka333  wokaka123


博文来自:www.51niux.com

三、sed的一些高级用法

上面记录的内容,进行一般的shell脚本工作已经可以了,但是要玩一些6的操作,还是要掌握一点高级的用法。

首先我们要了解sed的工作原理:

sed是一个流文本处理工具,从文件到文件尾读取,一次只读取一行,并完成一系列操作才继续读取下一行。sed维护两个数据缓存区,一个是pattern space(模式空间),一个是hold space(保持空间)。它们初始都为空。模式空间是活跃缓冲区,每一次循环都会清空再存入下一行内容。保持空间是一个辅助的空间,不会在完成一个循环后清空,会一直保持,它的内容来自使用h,H,g,G命令得来。

高级命令:所谓的高级命令通常指模式空间 <---> 保持空间 行的交互,如何交换、覆盖、追加。除非使用特殊的命令,如”D”,否则pattern space会在两个循环之间被清空。而hold space则会保持不变,hold space的内容可以使用‘h’, ‘H’, ‘x’, ‘g’, ‘G’的命令来操作。

上面已经对高级命令做了解释,这里用一些实际例子来进行一下解释记录:

示例1(n读取下一行覆盖模式空间中的行):

# seq 11|sed 'n;d'

1

3

5

7

9

11

# seq 12|sed 'n;d'

1

3

5

7

9

11

#上面seq 11 和seq 12的输出结果是一致的,让我们捋一下。首先读取了第一行1到了模式空间里面,然后n是读取下一行的命令,1的下一行也就是2.默认动作是先输出模式空间中的行,再覆盖读取下一行,再指定d命令。所以就是1进入模式空间输出了,然后就是n命令把2读取到了模式空间,然后就执行了d是删除模式空间的内容(d命令在运行后直接执行下一个循环,所以它并不会执行之后的命令和打印模式空间),又将模式空间里面的2删除了,这样2就不输出了。然后3不是下一行,又相当于了第一行,4又相当于了下一行,以此类推。到了11读取进模式空间,运行命令n,不过读取不到下一行了,因为读取不到,所以sed退出。


示例2 (N读取下一行并追加到模拟空间中现有行后面来创建多行空间,新行和原有的行以\n分隔。sed将其看做一行处理。)

# seq 9|sed '{N;s/\n/\t/}' #跟# seq 9|sed '{N;s/\n/\t/g;p;d}'效果是一致的。通过这个例子可以比较清楚的了解这N的用法,先是1第一行放到了模式空间里面去,然后执行N命令,将1的下一行追加到了模式空间而不是覆盖哦。现在模式空间里面就是1\n2,然后紧跟着我们就将\n换行符改成了\t水平制表符,然后输出后,然后就往下读取3(系统读入时总是覆盖原有内容)以此类推,继续循环就是现在看到的效果。

1     2

3     4

5     6

7     8

9

# seq 5|sed 'N;a---'  #或者用这个例子查看一下,在执行完N之后,在模式空间的尾部附加一行---,所以模式空间的内容为“1\n2\n----”而不是“1\n---\n2\n---”

1

2

---

3

4

---

5

下面我们通过下面一个删除的例子,再加深一下这个N的使用:

# seq 11|sed 'N;d'

11

# seq 12|sed 'N;d'

# seq 9|sed 'N;d'

9


示例3 (n,N和d,D(D会删除模式空间内第一行,并且如果模式空间内容不为空,它会循环执行前面命令。直到为空才会执行下一循环)经常一起使用,这里展示一下D和d的不同)

# seq 6|sed '{N;d}'

# seq 6|sed '{N;D}'  #从这里可以看到D跟上面的d在最后的结果是有不同的。

6


示例4 (-n(把模式空间中的内容关闭显示也就是不输出sed的处理结果)和-p(打印匹配到的所有输出)和-P(输出多行模式空间的第一部分)的在这里的作用):

# seq 5|sed -n '{N}'  #加-n 就不显示了,但是单加-n没什么意思,一般都是跟-p在一起使用的。

# seq 5|sed -n '{N;p}'  #因为到5,下面就么有了所以N的执行过程到4就算结束了,所以这样一混合使用显示的就是1\n2 ,3\n4

1

2

3

4

# seq 5|sed -n '{N;P}' #而P呢,是显示多行的第一行部分,所以就是显示的1,3

1

3


示例5 (-x  交换模式空间与保持空间内的内容)

# seq 4|sed '/3/{x;}' #这里加了过滤条件,到了3才会执行我们的x命令,因为到了3模式空间里面的内容是3,而保持空间里面是一个空内容,所以两者替换,我们模式空间里面的内容就成了空,所以结果是1,2,空,4
1
2

4
# seq 4|sed -n '/3/{x;p}' #这下面是一个空行,是为了再跟示例4对应一下,只是将sed替换的部分显示了出来,因为到了3,就让模式空间与保持空间进行了对调,而保持空间初始是空,所以这里替换部分就是一个空行。

# seq 5|sed '/3/!{x;}'  #这里就是当匹配条件不为3的时候执行后面的x命令,让模式空间与保持空间进行对换。


1

3

2

4

第一行:模式空间的1与保持空间的空白行对换,所以第一行打印的是1,然后现在保持空间里面是1

第二行:模式空间的2与保持空间的1对调,所以第二行打印的是1,然后现在保持空间里面是2

第三行:模式空间为3,触发了我们当匹配条件是3的时候不进行x命令操作,所以输出的是3,保持空间里面还是2

第四行:模式空间的4与保持空间的2对调,所以第四行打印的是2,然后现在保持空间里面是4

第五行:模式空间的5与保持空间的4对调,所以第五行打印的是4,然后保持空间里面的是5,不过已经没有下一行了,也就没有下一次的对换操作了,所以5也就没有输出


示例6 (-h 复制模式空间内容到保持空间以覆盖的形式 和 -H 复制模式空间内容到保持空间以追加的形式)

# seq 4|sed  'x;h'  #输出结果为四个空行,以为h是覆盖形式吗,先是把保持空间的空行对换过来,再执行h将空行覆盖回去,每次都如此,所以输出了四个空行。





# seq 4|sed  'x;H'  #输出结果是空行,1空行,21空行,321空行


1


2

1


3

2

1


第一步:模式空间的1与保持空间的空行对调,所以现在,模式空间里面是0,保持空间里面是1,然后复制模式空间里面的空行追加到1下面,所以保持空间最后是1空行,模式空间里面是0并输出。

第二步:模式空间的2与保持空间的1空行对调,所以现在,模式空间里面是1空行,保持空间里面是2,然后复制模式空间里面的1空行追加到2下面,所以保持空间最后是21空行,模式空间里面是1空行并输出。

第三步:模式空间的3与保持空间的21空行对调,所以现在,模式空间里面是21空行,保持空间里面是3,然后复制模式空间里面的21空行追加到3下面,所以保持空间最后是321空行,模式空间里面是21空行并输出。

第四步:模式空间的4与保持空间的321空行对调,所以现在,模式空间里面是321空行,保持空间里面是4,然后复制模式空间里面的321空行追加到4下面,所以保持空间最后是4321空行,模式空间里面是321空行并输出。


示例7 (g 把暂存缓冲区也就是保持空间里的内容复制到模式空间,覆盖原有的内容 和 G 把暂存缓冲区也就是保持空间的内容追加到模式空间里,追加在原有内容的后面)

# seq 4|sed '/4/!{g;}'  #下面是1-3行输出的是空行,第四行为4




4

# seq 4|sed '/4/!{G;}'  #下面第一部分是1空行,第二部分是2空行,第三部分是3空行,第四部分是4

1


2


3


4

#这里没多少好解释的,示例6已经解释的很详细了。G和H正好是相反。H是追加模式空间内容到保持空间,G是追加保持空间的内容到模式空间。

# seq 4|sed  '{1!H;$G}'  #第一行不执行H,也就是第一行不执行从模式空间复制内容追加到保持空间的操作,二三四行执行H。尾行还要执行G操作。

1

2

3

4


2

3

4

第一步:第一行不执行H,所以现在模式空间是1,保持空间是空行,所以模式空间的1输出

第二步:第二行执行H,所以现在模式空间是2,复制模式空间的2追加到保持空间,所以现在保持空间是空行2,模式空间是2并输出

第三步:第三行执行H,所以现在模式空间是3,复制模式空间的3追加到保持空间,所以现在保持空间是空行23,模式空间是3并输出

第四步:第四行执行H,所以现在模式空间是4,复制模式空间的4追加到保持空间,所以现在保持空间是空行234,尾行又执行G,所以又将保持空间的空行234追加到模式空间4的尾部,所以最后模式空间是4空行234并输出。


示例8 (混合使用)

# cat  3.txt   #这是要操作的文本

A

a

B

b

C

c

D

d

# sed -n '{n;p}' 3.txt  #显示偶数行

a

b

c

d

# sed -n '{N;P}' 3.txt   #显示奇数行

A

B

C

D

# sed -n '{h;n;p;g;p}' 3.txt  #这个就是先把a覆盖到保持空间,后执行n命令将下一行放到模式空间空间,p将模式空间里面的内容打印出来也就是第二行的a,然后执行g命令再将保持空间里面的A覆盖会模式空间,再执行p打印。

a

A

b

B

c

C

d

D

# sed -ne 'H;${x;s/\n/ /g;p}' 3.txt   #行列转换

 A  a  B  b  C  c  D  d

# seq 10|sed -ne 'H;${x;s/\n/+/g;s/^+//;p}'  #s/^+//是将1前面的+号去掉

1+2+3+4+5+6+7+8+9+10

# seq 10|sed -ne 'H;${x;s/\n/+/g;s/^+//;p}'|bc  #直接就可以用bc进行数字间的求和了

55


示例9(加-e来进行操作)

# cat  test.txt  #这是实验文本,奇数行是用户,偶数行是邮箱

xiaoming

@51niux.com

shauiqi

@51niux.com

shuaiguo

@51niux.com

ddd51niux

@qq.com

如果我们只想取出@51niux.com的用户都是谁呢?

# sed -n -e '/@51niux.com/!h' -e '/@51niux.com/{x;p}' test.txt  #第一段的意思是包含有@51niux.com的行不覆盖到保持空间去,那就一行一行来白。第一行是xiaoming,不在我们的匹配范围,所以覆盖到保持空间去,现在保持空间就是xiaoming,因为我们开头用了-n,所以没有p是不输出的。所以目前第一行是木有输出的。那往下走到了第二行,然后我们匹配条件有一个如果是@51niux.com的行,跟保持空间对换并且执行打印操作,那么保持空间的xiaoming就又换回到了模式空间,并执行替换打印操作,所以第一行的xiaoming是输出的,而第二行的@51niux.com是不打印的。以此类推,到了第7行,又丢到保持空间了,第八行一看不是@51niux.com,所以什么对调啊打印啊都没有执行,ddd51niux就被关在了保持空间没有打印出来,@qq.com是进入到了模式空间,但是没有p命令啊,页面是不显示的。

xiaoming

shauiqi

shuaiguo

如果我们想取出qq的用户并让它组成邮箱的形式呢?

# sed -n -e '/@qq.com/!h' -e '/@qq.com/{H;x;s/\n//p}' test.txt   #第一段就是不是@qq.com的行就执行h,然后每一行都存在了模式空间然后覆盖到保持空间去,然后到了第七行依旧,所以保持空间里面现在是ddd51niux,然后第八行匹配到了,就执行将@qq.com追加到了保持空间,就成了ddd51niux.com\n@qq.com,然后x对调,然后模式空间就变成了ddd51niux\n@qq.com,然后我们将\n替换掉,再打印,就变成了我们想要的结果。

ddd51niux@qq.com


好了写到这里,基本可以写一些负载的sed脚本了,就是根据文本的规则多琢磨琢磨规则,怎么去写这个逻辑。


博文来自:www.51niux.com

四、sed的循环标签

编程语言很大的特点是循环语句实现重复执行,sed中没有for\while\until语句,而是通过定义标签,调用标签来实现循环这种功能的。标签名调用有b调用,t调用,T调用。

一般格式为:

sed -n ':标签名  范围1 命令1;/模式/b 标签名' filename

示例1:

# sed -n ' s/xiaoming/Xiaoming/;p' test.txt  #因为我们没有加g,所以只替换第一行

Xiaoming  xiaoming

# sed -n ':again s/xiaoming/Xiaoming/;/xiaoming/b again;p' test.txt  #我们用标签的形式也可以实现g的效果。

Xiaoming  Xiaoming

示例2:

# cat 5.txt  #这是实验文本,如何让文本中的空行只保留一行空行

aaa



123




#1a



ddd


eee

# sed -rn 'h;n;:a;H;n;$!ba;g;s/(\n){2,}/\n\n/g;p' 5.txt 

aaa


123


#1a


ddd


eee


作者:忙碌的柴少 分类:linux文本编辑工具 浏览:1951 评论:0
留言列表
发表评论
来宾的头像