shell程序设计(十)sed高级应用

Bash ginotang 1035℃ 0评论

Sed执行流程

sed内部维护两个数据空间,一个是pattern space,另一个是hold space。初始状态下,它们都是空的。

当sed从流中读取一行数据,先把这行数据放进pattern space,然后执行指定的命令,一旦对当前行的全部命令执行完毕,就会把pattern space中的数据输出到屏幕,最后删除pattern space中的数据,继续读取下一行数据执行,这是一个完整的执行循环。

hold space是一个辅助的数据空间,只有在我们显式往这个空间中存数据的时候它才会被使用,hold space在每个循环过程中会保留下数据,我们可以使用下面的命令操作hold space中的数据:

  • h:               用pattern space的数据替换hold space的数据
  • H:              把pattern space的数据追加到hold space中
  • g:               用hold space的数据替换pattern space的数据
  • G:              把hold space的数据追加到pattern space中
  • x:               交换pattern space和hold space中的数据

处理多行数据

sed是基于行的文本编辑器,对于多数命令,它都只能针对一行数据进行处理。如果要处理两个单词落在不同行的情况,例如一个在第一行的结尾,另一个在第二行的开始。则需要用到sed提供的其他几个命令:N、D、P。

  • N:把下一行添加都当前行,即两行作为一个分组处理
  • D:删除分组中的第一行
  • P:打印分组中的第一行

n命令和N命令的区别

小写n的作用也是把下一行数据读入到pattern space,但读入之前会清空当前pattern space中的数据;而N则先读入一行数据,然后把下一行数据追加到当前pattern space,换句话说,N可以把两行数据一起读入到pattern space,两行数据之间用'\n'分隔。当没有数据继续输入,sed退出。

p命令和P命令的区别

小写p命令的作用是打印pattern space中的所有数据;大写P命令的作用是打印pattern space中的第一行数据。它们通常都会和-n参数一起使用。

d命令和D命令的区别

小写d命令用于删除patter space中的数据,并马上进行下一个sed循环;大写D只删除pattern space中的第一行数据,如果pattern space中还有数据,则从头继续执行,不跳到下一个sed循环

实际操作

现有一文件,名为test.txt,它的内容如下:

N和D

由于D命令用于操作N命令载入pattern space的数据,因此它们通常是一起使用。

上例的作用是:当匹配到two的时候,就把匹配行的上一行删除。在这个过程中,two实际上是被匹配了两次,因此有两行数据被删除了。下面是命令的执行流程图(图中省略了sed没有下一行可读的情况):

sed execute circle

流程解释:

第一次:N读入了两行,分别是line one和line two,由于/two/匹配到第二行,因此,line one被删除,现在pattern space中仍然有line two,因此D返回到顶层继续执行match,/two/依然被匹配到,因此line two也被删除。

第二次:现在pattern space中没有任何数据,N读入两行,即line three和line four,这个时候没有任何匹配,清空pattern space中的数据,然后进入下一次循环

第三次:只有一行数据可读,即line five,它没有下一行,因此N没有数据可读,sed退出。

当然,多行任务的意义在于跨行匹配,这样只匹配单行数据确实没有多大意思。下面是一个跨行匹配的例子:

例子中查找System\nAdministrator 替换成Desktop\nUser,由于它们处于不同的行中,因此需要使用换行符换行。处于同一行的System Administrator不会被替换。

Hold Space使用实例

最经典的例子莫过于反转文本行,例如:

将文本:

转换为

和我们常见的交换两个变量的值类似,我们需要引入第三个变量作为临时储存空间,而Hold space就是这个临时空间。

假如保存数据的文件名为reverse.txt,可以使用下面的命令交换数据的排序:

sed的执行顺序大致如下:

sed reverse line

从上图可以看出,除了第一行数据先放入到hold space,处理其他行的时候都是先从hold space中取数据,然后和pattern space中的数据拼接,具体如下:

  • 第一次:读入line one到pattern space,然后马上存放到hold space中,进入下一个循环
  • 第二次:读入第二行,现在pattern space中有line two,因为不是第一行,因此从hold space中取出line one Append到line two,现在pattern space中数据是line two\nline one。然后把它们放到hold space等待处理。
  • 第三次:重复第二次的步骤,读入第三行,取出hold space中的line two\nline one Append到line three,拼接完重新放回到hold space。现在hold sapce中的数据是line three\nline two\nline one
  • 第四次:重复第二次步骤,读入第四行,取出第三个步骤中完成的数据进行拼接,达到最后一行,输出所有pattern space中的数据。

分支(branch)和测试(test)

sed命令默认从上往下顺序处理数据,它没有像if…else,while这样的控制结构,如果有这样的需求,通常建议配合awk命令使用。但是如果坚持只使用sed,那么可以使用branch或者test模仿类似if…else的功能。

branch和test类似一些编程语言中的的goto语句,通过设置一个标签(label),可以达到随意跳转的目的。

使用branch模仿if…else语句

配合正则匹配来做跳转,可以轻易模仿if..else结构:

如果用伪代码的方式来描述脚本的行为,是这样的:

使用branch模仿循环结果

label的设置方式是:

:labelname

上例先设置一个label,然后打印pattern space中的内容,最后返回到start标签,不断重复这个步骤,所以,这是一个死循环,必须使用ctrl+c结束。

使用循环去掉句子中的所有逗号

由于没有结束条件,即使所有逗号都被替换后,命令也没有终止,直到ctrl+c。

添加结束条件:

添加了跳转条件:当句子中找到逗号(,)的时候才进行跳转,这样脚本就可以正常退出。

使用测试(test)

test是基于替换的命令:只有在当前行发生成功替换的操作时,test(t)才会执行跳转。test命令有两个,一个小写t,另一个是大写T,大写T的功能和小写t正好相反,它是在当前行没有发生成功替换时才跳转。

如果没有设置label,branch和test都是跳到命令集的最后,然后执行下一个循环。

使用伪代码表示:

test命令也可以模仿循环结构:

由于test自带退出条件,因此我们不用再另外添加退出条件。

使用&符号

&符号用于表示正则匹配成功的结果。例如:

为匹配成功的字符串添加一个双引号。这里&表示.at匹配出来的cat和hat。

分组

可以使用圆括号进行分组匹配,每个匹配成功的分组都有一个分组号码

例子中我们匹配字符串是System Administrator manual,它们当中,使用圆括号划分了两个组,分别是System和manual,而它们各自都有一个分组号码:\1\2\1\2就是代表System和manual。

输出结果:

由于圆括号是ERE中的特殊符号,所以要转义。也可以使用sed的-r选线开启ERE模式。开启之后圆括号就不需要转义。

 

转载请注明:Pure nonsense » shell程序设计(十)sed高级应用

喜欢 (0)
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x