主要内容
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,它的内容如下:
1 2 3 4 5 6 |
[normal@centos7-server tmp]$ cat test.txt line one line two line three line four line five |
N和D
由于D命令用于操作N命令载入pattern space的数据,因此它们通常是一起使用。
1 2 3 4 |
[normal@centos7-server tmp]$ sed 'N; /two/D' test.txt line three line four line five |
上例的作用是:当匹配到two的时候,就把匹配行的上一行删除。在这个过程中,two实际上是被匹配了两次,因此有两行数据被删除了。下面是命令的执行流程图(图中省略了sed没有下一行可读的情况):
流程解释:
第一次: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退出。
当然,多行任务的意义在于跨行匹配,这样只匹配单行数据确实没有多大意思。下面是一个跨行匹配的例子:
1 2 3 4 5 6 7 8 9 |
[normal@centos7-server tmp]$ cat meeting.txt On Tuesday, the Linux System Administrator's group meeting will be held. All System Administrators should attend. [normal@centos7-server tmp]$ sed 'N; s/System\nAdministrator/Desktop\nUser/' meeting.txt On Tuesday, the Linux Desktop User's group meeting will be held. All System Administrators should attend. |
例子中查找System\nAdministrator
替换成Desktop\nUser,由于它们处于不同的行中,因此需要使用换行符换行。处于同一行的System Administrator不会被替换。
Hold Space使用实例
最经典的例子莫过于反转文本行,例如:
将文本:
1 2 3 |
this is line one this is line two this is line three |
转换为
1 2 3 |
this is line three this is line two this is line one |
和我们常见的交换两个变量的值类似,我们需要引入第三个变量作为临时储存空间,而Hold space就是这个临时空间。
假如保存数据的文件名为reverse.txt,可以使用下面的命令交换数据的排序:
1 2 3 4 5 |
[normal@centos7-server tmp]$ sed -n '1!G; h; $p' reverse.txt This is the last line. This is the second data line. This is the first data line. This is the header line. |
sed的执行顺序大致如下:
从上图可以看出,除了第一行数据先放入到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结构:
1 2 3 4 5 6 7 |
[normal@centos7-server tmp]$ sed '{/second/b jump1; s/This is the/No jump on/ :jump1 s/This is the/Jump here on/}' data2.txt No jump on header line. No jump on first data line. Jump here on second data line. No jump on last line. |
如果用伪代码的方式来描述脚本的行为,是这样的:
1 2 3 4 |
if not found 'second' then; s/This is the/No jump on/ else s/This is the/Jump here on/ |
使用branch模仿循环结果
label的设置方式是:
:labelname
1 2 3 4 5 6 7 |
[normal@centos7-server tmp]$ cat data2.txt This is the header line. This is the first data line. This is the second data line. This is the last line. [normal@centos7-server tmp]$ sed -n ':start;p; b start' data2.txt #重复打印第一行 |
上例先设置一个label,然后打印pattern space中的内容,最后返回到start标签,不断重复这个步骤,所以,这是一个死循环,必须使用ctrl+c结束。
使用循环去掉句子中的所有逗号
1 2 3 4 5 6 7 8 |
[normal@centos7-server tmp]$ echo "This, is, a, test, to, remove, commas." | sed -n ':start; s/,//p; b start' This is, a, test, to, remove, commas. This is a, test, to, remove, commas. This is a test, to, remove, commas. This is a test to, remove, commas. This is a test to remove, commas. This is a test to remove commas. ^C |
由于没有结束条件,即使所有逗号都被替换后,命令也没有终止,直到ctrl+c。
添加结束条件:
1 2 3 4 5 6 7 |
[normal@centos7-server tmp]$ echo "This, is, a, test, to, remove, commas." | sed -n ':start; s/,//p; /,/b start' This is, a, test, to, remove, commas. This is a, test, to, remove, commas. This is a test, to, remove, commas. This is a test to, remove, commas. This is a test to remove, commas. This is a test to remove commas. |
添加了跳转条件:当句子中找到逗号(,)的时候才进行跳转,这样脚本就可以正常退出。
使用测试(test)
test是基于替换的命令:只有在当前行发生成功替换的操作时,test(t)才会执行跳转。test命令有两个,一个小写t,另一个是大写T,大写T的功能和小写t正好相反,它是在当前行没有发生成功替换时才跳转。
1 2 3 4 5 6 7 8 9 10 11 |
[normal@centos7-server tmp]$ cat data2.txt This is the header line. This is the first data line. This is the second data line. This is the last line. [normal@centos7-server tmp]$ sed '{s/first/matched/; t; s/This is the/No match on/}' data2.txt No match on header line. This is the matched data line. No match on second data line. No match on last line. |
如果没有设置label,branch和test都是跳到命令集的最后,然后执行下一个循环。
使用伪代码表示:
1 2 3 4 5 |
if found first then; replace it with matched; exit to next circle else s/This is the/No match on |
test命令也可以模仿循环结构:
1 2 3 4 5 6 7 |
[normal@centos7-server tmp]$ echo "This, is, a, test, to, remove, commas." | sed -n ':start; s/,//p; t start' This is, a, test, to, remove, commas. This is a, test, to, remove, commas. This is a test, to, remove, commas. This is a test to, remove, commas. This is a test to remove, commas. This is a test to remove commas. |
由于test自带退出条件,因此我们不用再另外添加退出条件。
使用&符号
&符号用于表示正则匹配成功的结果。例如:
1 2 |
[normal@centos7-server tmp]$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g' The "cat" sleeps in his "hat". |
为匹配成功的字符串添加一个双引号。这里&表示.at
匹配出来的cat和hat。
分组
可以使用圆括号进行分组匹配,每个匹配成功的分组都有一个分组号码
1 |
echo "The System Administrator manual" | sed 's/\(System\) Administrator \(manual\)/\1 \2/' |
例子中我们匹配字符串是System Administrator manual,它们当中,使用圆括号划分了两个组,分别是System和manual,而它们各自都有一个分组号码:\1
和\2
。\1
和\2
就是代表System和manual。
输出结果:
1 |
The System manual |
由于圆括号是ERE中的特殊符号,所以要转义。也可以使用sed的-r
选线开启ERE模式。开启之后圆括号就不需要转义。
1 |
echo "The System Administrator manual" | sed -r 's/(System) Administrator (manual)/\1 \2/' |
转载请注明:Pure nonsense » shell程序设计(十)sed高级应用