GNU sed 4.5 版参考文档全文翻译 各命令和随带20个示例详细解析(六)

12 篇文章 2 订阅

GNU sed 一个流编辑器(六)

—— 版本 4.5,2018年3月30日

作者:Ken Pizzini, Paolo Bonzini
译者:浙江省杭州市 Samuel        

标签:Linux sed 4.5版本 帮助文档 参考文档 全文翻译 随带20个示例 详细解析

版权声明:本文为博主原创译文,未经博主允许不得转载。https://blog.csdn.net/qq_39785418/article/details/90032442

6、 高级sed:循环和缓冲

6.1 sed如何工作

  sed维护了两个数据缓冲区:一个是活动的模式空间,另一个是辅助的保持空间。两者初始化时都为空的。

  (译者:为什么命名为模式空间pattern space?我的理解是:在sed程序中,是否对输入行进行处理,取决于是否匹配行地址;以及如何处理——大量使用s替换命令,这些都涉及正则表达式的搜索模式。为什么是命名为活动?sed程序的处理基本上在模式空间中进行。保持空间hold space主要起一个辅助的临时保存数据的作用)

  sed是通过对每一输入行执行以下循环来操作的:

  首先,sed从输入流中读取一行,移去行尾的换行符,把它放入模式空间中。

  然后,执行脚本中包含的一系列命令。每个命令可以有一个与之相关联的地址,而地址是一种条件代码,在命令执行以前,先进行条件验证,只有验证通过,命令才会执行。

  再者,当执行到脚本的结尾时,除非使用了‘-n’选项,否则,模式空间中的内容会打印到输出流中,行末加上前面移去的换行符。(实际上,如果sed打印了不换行的行,那么当更多的文本被发送到相同的输出流时,它仍然会缺少换行地打印,这是“最意想不到的惊喜”,即使它并没有想把“sed - n p”这样的命令设计得与cat完全相同。)

  最后,开始下一轮循环处理下一输入行。

  除非使用了像‘D’这样特殊的命令,在两个循环中间会删除输出模式空间的部分内容。另一方面,保持空间的内容在循环中任然会保持它的数据不变。(参见命令“h”、“H”、“x”、“g”、“G”,用于在两个缓冲区之间移动数据)

6.2 保持缓冲区和模式缓冲区

  备忘录

6.3 多行技术——使用‘D’、‘G’、‘H’、‘N’、‘P’等命令处理多行

  使用‘D’、‘G’、‘H’、‘N’、‘P’等命令可以在一个缓冲区内处理多行。它们的作用类似于小写对应项(d、g、h、n、p),除了这些命令在添加或去掉数据时,要考虑内嵌的换行符——即允许在模式空间和保持空间中追加或移除行。

  它们操作如下:

H		先在保持空间中追加一个换行符,再追加模式空间的内容。

N		先在模式空间中追加一个换行符,再追加从输入流读入的下一行内容。

P		打印模式空间中的内容直到第一个换行符(含)为止。

  下面示例演示N和D命令的操作:

$ seq 6 | sed -n 'N; l; D'
1\n2$
2\n3$
3\n4$
4\n5$
5\n6$

   上面的命令解释如下:

sed首先读入第一行到模式空间(例如:‘1’)。

在每个循环开始后,N命令会追加一个换行符和下一输入行到模式空间(例如:第一轮循环中在模式空间形成:‘1\n2’)。

l(译者:当成list)命令会以一种可视的明确的形式打印模式空间的内容。(译者:例如:“1\n2$”:换行符打印成‘\n’,
	行尾加一个‘$’,把一些不可见的内容显示出来。)
	
然后D命令会把模式空间中的第一个换行符及前面的内容删除。(第一轮循环后,遗留内容是只有‘2’。)

下一轮循环,N命令会追加一个换行符和下一行到模式空间(例如:‘2\n3’)。

   处理段落这样的块文本(下面的示例中是用一空行来分隔的)、而不是一行行文本的通用技术使用以下结构:

sed '/./{H; $!d}; x; s/REGEXP/REPLACEMENT/'
第一个表达式‘/./{H; $!d}’操作所有非空行(译者:句点匹配任意单个字符,但是在多行模式下,换行符例外)。它具体执行过程是:
	H命令先在保持空间中追加一个换行符,然后追加到模式空间内容,再删除模式空间,除非到了最后一行(‘$!’)不删除,否则删除后
	立即开始下一轮循环。

后面的两个命令x和s,只有在遇到空行时才执行。x命令把累计在保持空间中的内容与模式空间的内容(实际上是空的)相互交换。然后,
	‘s///’命令对该段落中的所有文本(包括内嵌的换行符)进行替换操作。

   下面的例子演示了这个技术:

$ cat input.txt 
a a a aa aaa
aaaa aaaa aa
aaaa aaa aaa

bbbb bbb bbb
bb bb bbb bb
bbbbbbbb bbb

ccc ccc cccc
cccc ccccc c
cc cc cc cc

$ sed '/./{H; $!d}; x; s/^/\nSTART-->/; s/$/\n<--END/' input.txt 

START-->
a a a aa aaa
aaaa aaaa aa
aaaa aaa aaa
<--END

START-->
bbbb bbb bbb
bb bb bbb bb
bbbbbbbb bbb
<--END

START-->
ccc ccc cccc
cccc ccccc c
cc cc cc cc
<--END

  更多有注释的示例见第7.7节[跨多行的文本搜索]和7.8节[行长度调整]。

6.4 分支和流程控制

  分支命令b、t和T能够改变sed程序的执行流程。(译者:可以把无条件分支b命令理解成C语言或VB语言的goto语句,而条件分支命令t和T可以看成其他编程语言的if/then语句。)

  默认情况下,sed在读取一输入行到模式缓冲区后,按顺序执行脚本中的所有命令。没有地址的命令影响所有行。有地址的命令只会影响匹配的行。参见6.1节[循环执行]和4.1节[地址摘要]。

  sed不支持传统的if/then结构。取而代之,一些命令可以用作条件或更改默认流程控制:

d			删除(delete)或清理当前模式空间,并在没有执行剩余命令和不打印模式空间的情况下,开启下一轮循环。

D			删除模式空间中第一个换行符及其前面的内容,并在没有执行剩余命令和不打印模式空间的情况下,开启下一轮循环。

[addr]X
[addr]{X; X; X}
/regexp/X
/regexp/{X; X; X}
			地址和正则表达式可以用作if/then条件。如果[addr]匹配当前模式空间,执行这个或这些命令(译者:X是命令的占位符)。
			例如,命令‘/^#/d’的意思是:如果当前模式匹配正则表达式‘^#’——即以#开头的行,那么执行d命令——删除该行,也没有打印
			它,	然后立即重启下一轮循环。
			
b			无条件分支——总是跳转到一个标签处(译者:如果省略标签,可以理解成跳转到脚本的结尾,开始下一轮循环),逃过或者重复
			执行其他的命令,也不会重启新一轮循环。如果结合一个地址,分支可以在匹配的行上有条件地执行。
			
t			条件分支——即只有在上一输入行上成功执行‘s///’命令,或者另一个条件分支被执行,才会跳转到一个标签处。

T			与t命令相似但条件相反——即只有在上一个输入行上执行失败,才会跳转到一个标签处。

  下面两个sed程序是等价的。第一个巧妙设计的示例使用b命令跳过匹配‘1’的行上执行s///命令。第二个示例使用一个地址,后带‘!’(表示取反),在不匹配‘1’的行上执行替换命令,然后,‘y///’命令仍然会在所有行上执行:

$ printf '%s\n' a1 a2 a3 | sed -E '/1/bx; s/a/z/; :x; y/123/456/'
a4
z5
z6
$ printf '%s\n' a1 a2 a3 | sed -E '/1/!s/a/z/; y/123/456/' 
a4
z5
z6
6.4.1 分支和循环

  b、t和T命令后面可以跟随一个标签(通常是一个单字符)。标签是通过组合一个冒号及一个或者多个字符来指定(例如:‘:x’)。如果标签省略了,分支命令就重启循环。注意分支跳转到一个标签和重启循环之间的区别:当重启循环时,sed先打印模式空间中的当前内容,然后,读取下一输入行到模式空间(译者:我把它理解为程序执行到达脚本结尾,这时,如果没有禁用自动打印,就会打印模式空间,然后读取下一个输入行);而跳转到一个标签不会打印模式空间,也不会读取下一个输入行,即使标签在程序的开始位置也是如此。

  下面的程序没有操作。程序中仅有的b命令没有标签可跳转,这样,仅仅是重启循环而已。每个循环,打印模式空间,读取下一输入行:

$ seq 3 | sed b
1
2
3

  下面的示例是一个无限循环——它不会终止,也不会打印任何内容。b命令跳转到“x”标签处,新的循环永远不会开始:

$ seq 3 | sed ‘:x; bx’
# 上面的命令要求GNU sed,它支持一个标签后可跟另外的命令,两者之间用分号(;)分隔,而不必是换行符。可移植性的等价脚本:
#     sed -e ‘:x’ -e bx

  分支命令通常与n或N命令互补:这两个命令都将会读取下一输入行到模式空间中,而不等待重新启动下一轮循环。在读取下一个输入行之前,n命令先打印当前模式空间,然后清空它;而N命令先向模式空间中追加一个换行符,再追加下一输入行。比较下面两个示例:

$ seq 3 | sed ‘:x; n; bx’
1
2
3
$ sed 3 | sed ‘:x; N; bx’
1
2
3
  • 两个示例都不是无限循环,尽管从来没有开始一个新的循环。
  • 第一个示例,n命令先打印模式空间中的内容,然后清空模式空间,再者读取下一个输入行。
  • 第二个示例,N命令把一个换行符和下一输入行追加到模式空间中。这些行累积在模式空间中,直到没有更多的输入行可以读取,那么,N命令终止sed程序。当程序终止时,循环结束的动作被执行,整个模式空间打印出来。
  • 第二个示例要求GNU sed,因为它使用N命令的非POSIX标准行为。参见第10章“最后一行上执行N命令”段落[报告bugs]。
  • 为了进一步测试两个示例的不同,试一试下面的命令:
printf '%s\n' aa bb cc dd | sed ':x ; n ; = ; bx'
aa
2
bb
3
cc
4
dd

  (译者:sed先读取第一行“aa”,遇到n命令,打印“aa”,且清空模式空间,然后读取下一行“bb”到模式空间中;命令=打印行号,这时行号已经是2,不是1,并换行;遇到b命令跳转到x标签位置,然后继续前面的操作)。

printf '%s\n' aa bb cc dd | sed ':x ; N ; = ; bx'
2
3
4
aa
bb
cc
dd

  (译者:sed先读取第一行“aa”,遇到N命令,读取下一行“bb”,并把一个换行符和这一行追加到模式空间中——即:“aa\nbb”;命令=打印行号,同上一个示例行号已经是2,并换行;遇到b命令跳转到x标签位置,然后继续前面的操作。这样每次b跳转前会打印编号,而输入的行全部累积在模式空间中,到N命令没有可读取的时候,sed结束前打印模式空间的包含换行符的多行——“aa\nbb\ncc\ndd”。)

printf '%s\n' aa bb cc dd | sed ':x ; n ; s/\n/***/ ; bx'
aa
bb
cc
dd

  (sed先读取第一行“aa”,遇到n命令,打印“aa”,且清空模式空间,然后读取下一行“bb”到模式空间中;由于模式空间内为空,无法匹配到换行符,替换命令不会执行;遇到b命令跳转到x标签位置,然后继续前面的操作)

printf '%s\n' aa bb cc dd | sed ':x ; N ; s/\n/***/ ; bx'
aa***bb***cc***dd

  (译者:sed先读取第一行“aa”,遇到N命令,读取下一行“bb”,并把一个换行符和这一行追加到模式空间中——即:“aa\nbb”;由于匹配“\n”成功,所以用字符串“***”替换匹配到的内容——模式空间内的内容为:“aa***bb”;遇到b命令跳转到x标签位置,然后继续前面的操作。这样每次遇到b命令跳转前都会执行“s///”命令,把“\n”替换掉,而被替换后内容全部累积在模式空间中,到N命令没有可读取的时候,sed结束前打印模式空间的内容——“aa***bb***cc***dd”。)

6.4.2 分支命令示例:跳转行

  作为一个真实使用分支命令示例,考虑“带引用可打印”(quoted-printable)文件,通常用于对电子邮件进行编码。在这些文件中,长行被拆分,并在行尾用一个由单个“=”字符组成的软换行符进行标记:(参考网页如下:https://en.wikipedia.org/wiki/Quoted-printable)

  (译者:quoted-printable是带引号可打印或QP编码:是指一种使用可打印的ASCII字符(字母数字和等号“=”)在7位数据路径上传输8位数据的编码,或通常在不能正确处理8位字符串编码的介质上传输8位数据的编码。从历史上看,电子邮件通常涉及不能处理8位字符串编码,因为各种介质被用来传输消息,有时在比互联网上使用更多。)

$ cat jaques.txt
All the wor=
ld’s a stag=
e,
And all the=
men and wo=
men merely =
players:
They have t=
heir exits =
and their e=
ntrances;
And one man=
in his tim=
e plays man=
y parts.

  下面的程序使用一个地址表达式“/=$/”作为条件,如果当前模式空间的行内容以“=”结尾,就使用N命令读取下一输入行,(译者:并把一个换行符和该行追加到模式空间中,)然后,使用s命令把所有的“=\n”字符序列予以清理,然后,遇到无条件的b命令导致在没有重启新一轮循环前提下,流程跳转到程序的开始处。如果模式空间没有以“=”结尾,不会执行这个命令组,那么默认动作会执行——打印模式空间,且开启新一轮循环:

$ sed ':x; /=$/{N; s/=\n//g; bx}' jaques.txt 
All the world’s a stage,
And all themen and women merely players:
They have their exits and their entrances;
And one manin his time plays many parts.

  下面是一个方法稍微不同的替代程序。除了最后一行以外的所有输入行,N命令都会把一个换行符和下一行追加到模式空间中。通过s命令删除软换行符(即在行尾的“=\n”)。如果替换成功(意味着模式空间中有了一应该被连接的行),那么,条件分支命令t会使流程跳转到程序的开始处,也没有重启循环。如果这个替换失败(意味着没有软换行符),t命令不会跳转。然后,P命令会打印第一个换行符及其前面的内容,且D命令会删除这个换行符及其前面的内容。(想要学习更多有关N、P和D命令参见6.3节[多行技术])。

$ sed ':x; $!N; s/=\n//; tx; P; D' jaques.txt

  结果与前面一样。

  想学习更多的行连接示例参见7.1节[连接行]。

[GNU sed 4.5 版参考文档全文翻译 各命令和随带20个示例详细解析(七)] (https://blog.csdn.net/qq_39785418/article/details/90080360)

  如果您觉得译文对您有帮助,不妨给个  微信打赏   翻译不易,各位的支持,能激发和鼓励我更大的热情。谢谢!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值