- 论坛徽章:
- 0
|
本帖最后由 smallpboy 于 2012-08-24 11:06 编辑
上次贴了一部分sed的内容,今天把全部的内容都贴出来。这两天没上cu,看到版主及其他大哥们的留言,小弟微微改下语句及排版等。让这文章再混个前排,罪过。罪过。
1、b/t/T的区别
所谓的标签,其功能就像c++里的“loop:”和“goto”。“b”操作的范例如下:
[smallpboy @local tmp]$ cat file
111
222
[smallpboy @local tmp]$ cat file | sed –n ‘/^1/b f;p;:f;p’
111
222
222
仔细看sed里面的内容,”/^1/b f”这个就是说匹配开头字母是1的,‘b’后面跟的‘f’实际上就想一个旗标,它标志着‘b’操作跳转到哪。这不后面不出现了”:f;p”语句么,‘:f’就是这个旗标,‘p’表示打印pattern space内容。所以,行首如果是‘1’的话,就会跳过一个‘p’操作(第一个被跳过),这样解释能看懂输出结果了吧。
接下来是t/T的范例:
[smallpboy @local tmp]$ cat file | sed –n ‘s/^1/&/;t f;p;:f;p ’
111
222
222
[smallpboy @local tmp]$ cat file | sed –n ‘s/^1/&/;T f;p;:f;p ’
111
111
222
t/T操作的区别看出来吧?‘t’操作表示前面的‘s/1^/&/’匹配成功,就跳到‘f’那去,‘T’操作则正好跟它相反。‘&’是什么东西?就不告诉你啊,就不告诉你。
“b/t/T”操作的功能就都说啦,是不是很简单? 对了,如果你不在“b/t/T”后面加上旗标‘f’的话,它默认是直接跳到脚本末尾。你可以试试如下命令:
[smallpboy @local tmp]$ cat file | sed –n ‘s/^1/&/;T;p;:f;p ’
111
111
看清楚哦,‘T’后面可没有‘f’咯,所以当‘T’操作满足时,直接跳到脚本尾了,什么输出都没有。而后面这个‘:f’你尽管保留,有它没它都不影响脚本。
2、p/P操作
这两个其实没啥说的,‘小p’表示打印pattern space全部内容。‘大P’表示打印pattern space的第一行。看下面的例子:
[smallpboy @local tmp]$ cat file
111
222
[smallpboy @local tmp]$ cat file | sed –n ‘/.*/G;p’ #小p
111
222
[smallpboy @local tmp]$ cat file | sed –n ‘/.*/G;P’ #大p
111
222
‘G’操作你已经很熟悉了吧?是不是把hold space的内容添加到pattern space中?这样,上面的例子能理解了吧。
3、d/D操作
‘d’操作没什么好讲的,一股脑将pattern space全部删除,这个‘D’操作就有不少人理解不过来了。好了,看我下面的例子你就清楚啦。
[smallpboy @local tmp]$ seq 4 | sed ‘$!N;d’
[smallpboy @local tmp]$
没有输出?对的,这个好理解吧!‘$!N’意思是如果还没读到最后一行,就继续读入一行(这个不用解释吧,不清楚试试’$!‘、'3!'这些语句去吧。)。
[smallpboy @local tmp]$ seq 4 | sed ‘$!N;D’
[smallpboy @local tmp]$
还是没有输出?对,因为这个‘D’是循环地一行一行删除pattern space内容,如果没有限制它跟‘d’功能是一样的。限制?怎样限制呢?限制什么东西,是pattern space还是文本行?
[smallpboy @local tmp]$ seq 4 | sed ‘$!N;4D’
1
2
[smallpboy @local tmp]$ seq 4 | sed ‘$!N;3D’
1
2
3
4
上面这两个能理解么?‘4D’意思就是第4行执行‘D’操作(原来限制的对象是文本),即sed你读到第四行的话我就删除pattern space第一行,再删第二行。所以它输出1\n2。我相信第一个你也许能理解,但这第二个真是不好理解啊。对吧?
‘3D’ 的意思是sed你读到第3行的话,我执行‘D’操作,很可惜啊。当sed读到第3行后,立刻又执行了‘$!N’操作,所以执行“3D”时,sed已经读到第4行了。所以这个‘D’不被执行啦。所以输出是上述结果,这下你懂了吧。在这条语句中,奇数行根本就取不到它,你写‘1D‘、’3d‘全判为假。这样,下面的例子你也能理解了。
[smallpboy @local tmp]$ seq 4 | sed ‘N;d’
[smallpboy @local tmp]$ seq 5 | sed ‘N;d’
5
第一个例子没有输出,当sed共读入四行时,他们两行两行配对,所以都被‘d‘了。而第二个例子中,当sed读到第5行后,执行’N‘操作,发现没有了(就算出错了哈),直接跳到脚本尾了。所以,它输出5。不信,你动手试试看啊。
好了,上面说了一堆,还没有讲到“D”操作。还有一个很重要的问题就是,我就是想要‘D’只删除pattern space的第一行,那要怎么办。man sed里‘D’的解释是:
delete up to the first embedded newline in the pattern space.start next cycle,but skip reading from the input if there is still data in the pattern space.
删除模式空间的第一个新行,开始下一个循环,模式空间有数据的话,不继续读入input数据,也就是继续循环‘D’操作。
[smallpboy @local tmp]$ seq 4 | sed –n ‘$!N;D;p’
2
4
上述命令‘$!N’读入“1\n2”进来,然后执行‘D’操作,删除pattern space第一行,然后‘p’操作把它打印出来。 你也是不是跟我一样想的呢?
可实际上,上述语句并没有任何输出。‘D’操作会循环执行删除pattern space一行的动作,知道pattern space没有数据为止。所以,pattern 里面的数据都被删光了。
再来看下面的例子:
[smallpboy @local tmp]$ seq 4 | sed –n ‘N;2D;p’
2
3
[smallpboy @local tmp]$ seq 4 | sed –n ‘N;3D;p’
1
2
3
4
不好意思,先把难的放第一个了。这个真的不好懂啊,我弄了好几小时才把‘D’弄明白啊。
‘sed’先读入‘1’(这算是sed的input,和'N'操作的input要分开),然后执行‘N’,读入‘2’。然后‘2D’,表示对第2行‘D’操作,这时,文本确实已经读到第2行,所以‘2D’成功执行把pattern space里‘1\n’给删除了。然后,注意了啊,‘D’操作并没有结束啊,pattern space还有‘2’呢,这时它继续从脚本开头执行,也就是‘N’操作,这时把‘3’读入(你可能会问,不是pattern space不为空,就不读input吗?你说的对啊,但是sed它确实没有主动读入input啊,而是‘N’操作读入了下一行,你把‘N’换成别的,那也就没有input数据了。)。好了,继续讲。读入‘3’后,到第三行了,‘2D’为假了,所以不执行‘D’操作了,终于轮到‘p’操作了,这时pattern space的结果是不是‘2\n3’啊,被打印出来。接着,sed主动读入‘4’,然后执行‘N’操作,发现没有下一行了,出错了就直接跳到脚本末尾,所以‘p’操作都不执行了。这样,‘4’也打印不出了。不信的话,你试试下面的脚本就能打印出‘4’来了。
[smallpboy @local tmp]$ seq 4 | sed –n ‘$!N;2D;p’
在这里我插一句,很多人就是因为测试时传进来参数的个数---奇数和偶数不同而导致产生不同的结果。
[smallpboy @local tmp]$ seq 5 | sed ‘N;d’
5
上述这个结果,居然出现了‘5’,看懂我前面讲的同学,你一定已经知道怎么回事了。输出的‘5’,它不是‘D’没有删除pattern space,而是执行‘$!N’时出错,直接跳过‘D’操作了。
说到这,‘D’操作也说完了。总的来说,注意的无非一点:
* 当类似‘N;2D;P’这样,‘D’满足条件的就会跳到脚本开头执行脚本,在这里也就是执行‘N’操作,然后再‘2D’操作,当‘2D’不满足条件后,才执行‘p’。
上回说到sed实现rev功能时,最后说到‘//D’操作真心不懂。现在咱再从头开始捋一遍。
整个语句:sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//;’
“/\n/!G;”在stdin中没有找到\n,就主动添上\n。
“s/\(.\)\(.*\n\)/&\2\1/;”中\(.\)先匹配一个字符,再匹配剩下所有字符。“&”指整个pattern space,\2和\1想必你也知道是什么东东。
[smallpboy @local tmp]$ cat file
123
file文件中“123”(上回是‘123\n’,其实你用’vi’编辑没有敲回车的话它并没有‘\n’,加不加‘\n’其实是有关系的,这里我不能再单独讲他们的区别,思维很混乱。)执行‘/\n/!G’,变成‘123\n’,然后执行‘s/\(.\)\(.*\n\)/&\2\1/;’
[smallpboy @local tmp]$ cat file | sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;’
123
23
1
上面可能读懂?“123\n”分别被匹配为\1(1)和\2(23\n),所以&\2\1的结果为‘123\n23\n1’。
[smallpboy @local tmp]$ cat file | sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D’
上面说到第一次执行s….后,pattern space为‘123\n23\n1’,然后要执行‘//D’命令了,结果为23\n1。
然后到开头继续执行/\n/!G;,pattern space(旧的)不变,然后执行s….后,新的pattern space为23\n3\n21(\1为2,\2为3\n,&为23\n),然后执行//D,结果为3\n21。
再执行/\n/!G;,pattern space(下一操作s….会产生新的pattern space把这个给覆盖)不变,执行s….,结果变为3\n\n321(\1为3,\2为\n,&为3\n),再执行//D操作,结果为\n321。
继续执行/\n/!G;,pattern space不变,然后执行s….,发现匹配不了。执行//D操作,‘//’不匹配,也不执行‘D’操作。所以‘sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D’’的结果为‘\n321’,最后执行‘s/.//’操作,所以最终打印出‘321’。
[smallpboy @local tmp]$ cat file | sed –n ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;P;//D’
123
23
3
[smallpboy @local tmp]$ cat file | sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D’
321
最后的,这个’//D’并没有匹配上‘\n321’,我到现在也不明白‘//D’这两个‘//’是什么意思,是匹配空字符吗?希望知道的朋友能在下面跟帖留言,大家一起互帮互助。
|
|