sed允许将多行内容读取到模式空间,这样你就可以匹配跨越多行的内容。本篇笔记主要介绍这些命令,它们能够创建多行模式空间并且处理之。其中,N/D/P这三个多行命令分别对应于小写的n/d/p命令,后者我们在上一篇已经介绍。它们的功能是类似的,区别在于命令影响的内容不同。例如D命令与d命令同样是删除模式空间的内容,只不过d命令会删除模式空间中所有的内容,而D命令仅会删除模式空间中的第一行。
一、读下一行:N
N命令将下一行的内容读取到当前模式空间,但是与n命令不一样的地方是N命令并没有直接输出当前模式空间中的行,而是把下一行追加到当前模式空间,两行之间用回车符\n连接,如下图所示:
说明:模式空间包含多行之后,正则表达式的^/$符号的意思就变了,^是匹配模式空间的最开始而非行首,$是匹配模式空间的最后位置而非行尾。
实例1:替换以下文本中的”Owner and Operator Guide”为”Installation Guide”
[root@localhost ~]# cat text Consult Section 3.1 in the Owner and Operator Guide for a description of the tape drives available on your system. [root@localhost ~]# sed '/Operator$/{N;s/Owner and Operator\nGuide/Installation Guide/}' text Consult Section 3.1 in the Installation Guide for a description of the tape drives available on your system.不过这个例子有两个局限:
● 我们知道Owner and Operator Guide分割的位置;
● 执行替换命令后,前后两行拼接在一起,导致这行过长;
第二点,可以这样解决:
[root@localhost ~]# sed '/Operator$/{N;s/Owner and Operator\nGuide/Installation Guide\n/}' text Consult Section 3.1 in the Installation Guide for a description of the tape drives available on your system.实例2:Owener and Operator Guide出现在多行的多个位置
[root@localhost ~]# cat text #原文文本 Consult Section 3.1 in the Owner and Operator Guide for a description of the tape drives available on your system. Look in the Owner and Operator Guide shipped with your system. Two manuals are provided including the Owner and Operator Guide and the User Guide. The Owner and Operator Guide is shipped with your system. [root@localhost ~]# sed 's/Owner and Operator Guide/Installation Guide/ > /Owner/{ > N > s/ *\n/ / > s/Owner and Operator Guide */Installation Guide> / > }' text Consult Section 3.1 in the Installation Guide for a description of the tape drives available on your system. Look in the Installation Guide shipped with your system. Two manuals are provided including the Installation Guide and the User Guide. The Installation Guide is shipped with your system.说明:这里我们首先将在单行出现的Owner and Operator Guide替换为Installation Guide,然后再寻找匹配Owner的行,匹配后读取下一行的内容到模式空间,并且将中间的换行符替换成空格,最后再替换Owner and Operator Guide。
看上去sed命令中作了两次替换是多余的。实际上,如果去掉第一次替换,再运行脚本,就会发现输出存在两个问题。一个是结果中最后一行不会被替换(在某些版本的sed中甚至不会被输出)。这是因为最后一行匹配了"Owner",执行N命令,但是已经到了文件末尾,某些版本就会直接打印这行再退出,而另外一些版本则是不作出打印立即退出。对于这个问题可以通过命令"$!N"来解决。这表示N命令对最后一行不起作用。另外一个问题是"look manuals"一段被拆为两行,而且与下一段的空行被删除了。这是因为内嵌的换行符被替换的结果。因此,sed中做两次替换一点也不是多余的。
二、删除行:D
该命令删除模式空间中第一行的内容,而它对应的小d命令删除模式空间的所有内容。D不会导致读入新行,相反它会回到最初的编辑命令,重要应用在模式空间剩余的内容上。
实例1:现在我们要删除文件text多余的空行,将多个空行缩减成一行。
文件内容:
[root@localhost ~]# cat text This line is followed by 1 blank line. This line is followed by 2 blank line. This line is followed by 3 blank line. This line is followed by 4 blank line. This is the end.使用d命令删除如下:
[root@localhost ~]# sed '/^$/{N;/^\n$/d}' text This line is followed by 1 blank line. This line is followed by 2 blank line. This line is followed by 3 blank line. This line is followed by 4 blank line. This is the end.说明:我们会发现一个奇怪的结果,奇数个数的相连空行已经被合并成一行,但是偶数个数的却全部被删除了。造成这样的原因需要重新翻译下上面的命令,当匹配一个空行是,将下一行也读取到模式空间,然后若下一行也是空行,则模式空间中的内容应该是\n,因此匹配^\n$,从而执行d命令会将模式空间中的内容清空,结果就是相连的两个空行都被删除。这样就可以理解为什么相连奇数个空行的情况下是正常的,而偶数个数就有问题了。
使用D命令删除如下:
[root@localhost ~]# sed '/^$/{N;/^\n$/D}' text This line is followed by 1 blank line. This line is followed by 2 blank line. This line is followed by 3 blank line. This line is followed by 4 blank line. This is the end.说明:D命令只会删除模式空间的第一行,而且删除后会重新在模式空间的内容上执行编辑命令,类似形成一个循环,前提是相连的都是空行。当匹配一个空行时,N读取下一行内容,此时匹配^\n$导致模式空间中的第一行被删除。现在模式空间中的内容是空的,重新执行编辑命令,此时匹配/^$/。继续读取下一行,当下一行依然为空行时,重复之前的动作,否则输出当前模式空间的内容。造成的结果是连续多个空行,只有最后一个空行是保留输出的,其余的都被删除了。这样的结果才是我们最初希望得到的。
三、打印行:P
P命令与p命令一样是打印模式空间的内容,不同的是前者仅打印模式空间的第一行内容,而后者是打印所有的内容。因为编辑命令全部执行完之后,sed默认会输出模式空间的内容,所以一般情况下,p和P命令都是与-n选项一起使用的。但是有一种情况是例外的,即编辑命令的执行流程被改变的情况,例如N,D等。很多情况下,P命令都是用在N命令之后,D命令之前的。这三个命令合起来,可以形成一人输入输出的循环,并且每次只打印一行:读入一行后,N继续读下一行,P命令打印第一行,D命令删除第一行,执行流程回到最开始重复该过程。
实例1:
[root@localhost ~]# echo -e "line1nline2nline3" | sed '$!N;P;D' line1nline2nline3 [root@localhost ~]# echo -e "line1\nline2\nline3" | sed '$!N;P;D' line1 line2 line3实例2:
[root@localhost ~]# echo -e "line1\nline2\nline3" | sed -n 'N;1P'说明:你可能期望打印第一行的内容,事实上并没有输出。原因是当N继续读入第二行后,当前行号已经是2了,行号只是sed在内部维护的一个计数变量而已,每当读入新的一行,行号就加一。
实例3:
[root@localhost ~]# echo -e "line1\nline2\nline3" | sed -n '$!N;=' 2 3实例4:N/P/D三个命令是如何配合使用
[root@localhost ~]# cat -n f1 1 The UNIX 2 System and UNIX 3 ... [root@localhost ~]# sed '/UNIX$/{N;s/\nSystem/ Operating &/;P;D}' f1 The UNIX Operating System and UNIX ...实例4执行流程图: