良玉的博客 点点滴滴,积水成河_良玉的博客_页游、手游linux运维工程师之路

详解linux中 sed-n/N/g/G/h/H/x……的用法及含义

    •先讲n/N的区别
ywlscpl大牛这么比喻:
如果sed的模式空间对应awk的$0
N相当于awk的{v=$0;next;$0=v"\n"$0}
n相当于awk的next

用shell来比喻的话
N是:  echo 下一行内容>>模式空间
n是:  echo 下一行内容>模式空间

[smallpboy @local tmp]$ seq 4 | sed ‘n;d’
1
3
pattern space先读入1,然后执行到n,把下一行2读入pattern space中并覆盖原本的1。然后pattern space中的内容(2)被删除(d操作),所以打印出1\n3。
[smallpboy @local tmp]$ seq 5 | sed ‘n;d’          你知道它输出什么吗? 
1
3
5
你猜对了吗?
[smallpboy @local tmp]$ seq 4 | sed ‘N;d’
[smallpboy @local tmp]$
什么都没输出?对,你没看错。pattern space先读入1,然后执行到N,把下一行添加到当前的pattern space中,pattern space内容为(1\n2,对否?),然后执行d操作被删除。接下去读入3(系统读入总是覆盖原有内容),执行N,pattern space 内容变为(3\n4),然后再被删除。
[smallpboy @local tmp]$ seq 7 | sed ‘N;d’       #你知道它输出什么吗?
7
你猜对了吗? 简单吧? 

        -n 参数指不自动打印pattern space内容,有的人对它理解不够导致测试n/N区别时被搞的越来越糊涂了。
[smallpboy @local tmp]$ seq 4 | sed –n ‘n’
[smallpboy @local tmp]$ seq 4 | sed –n ‘N’ 
上述两个不输出任何信息,不用说了吧! :)
[smallpboy @local tmp]$ seq 4 | sed –n ‘n;p’      
2
4
这个能理解么? 参数p指打印pattern space的内容。
[smallpboy @local tmp]$ seq 4 | sed –n ‘N;p’
1
2
3
4
这个呢?也懂!对吧。
[smallpboy @local tmp]$ seq 5 | sed –n ‘N;p’
1
2
3
4
这个呢?也懂吧?再看看这个
[smallpboy @local tmp]$ seq 5 | sed –n ‘n;p’
自己实验去。:)自己动手才记得牢。

•接下说x
sed有hold space和pattern space,x的意思就是将hold space和pattern space的内容交换。咱实验下,看懂上面n/N区别的你,看懂下面的实验就很轻松了。
[smallpboy @local tmp]$ seq 4 | sed –n ‘x;p’

1
2
3
第一行空白?没错,因为pattern space读入1时,hold space还是空的,两者一交换,pattern space不就成空了吗?而hold space持有1,对否?然后,hold space 和 pattern space再交换内容,此时pattern space得到1,hold space得到2,打印pattern space不就打印出1了。
序列最后是4,但只能打印出3,也能理解了吧。因为4在hold space里了。
        在sedline中有一题:在每个含有字符串regex的行上插入一行空白行。
[smallpboy @local tmp]$ seq 4 | sed ‘/3/{x;p;x;}’
1
2

3
4
看懂了没?在找到regex的那一行时,pattern space的内容不就是这匹配的整行数据么,跟hold space交换后,pattern space不就为空了,然后执行p操作输出空行,然后再跟hold space交换内容,这时pattern space不就又拿到其原本的数据了吗?你有可能会问,括号里为什么要有p操作啊,命令啥时自动输出pattern space内容啊?额,这个你可是试试把p去掉后结果是怎么样的啊,看下结果你就懂啦。或者是你看如下操作
[smallpboy @local tmp]$ seq 4 | sed ‘/3/{x;p;x;d}’
1
2

4
我们的操作都是括号里面,所以输出删除等操作由你自己决定啦。这样解释x操作能懂了吧?

•接下去讲h/H,分别为复制(覆盖)/添加pattern space到hold space。
这个没什么例子能单独举的呢。不行?必须要例子。。额。好吧,看下面。
[smallpboy @local tmp]$ seq 4 | sed ‘x’       #还记得结果吗?跟sed –n ‘x;p’一样的哦。

1
2
3
[smallpboy @local tmp]$ seq 4 | sed ‘h;x’
1
2
3
4
很容易懂吧?h操作把pattern space内容复制到hold space去了。x操作交换它俩的内容,两者一模一样,交换个啥啊。:)
[smallpboy @local tmp]$ seq 4 | sed ‘x;h’




额,我浪费版面?额,我哭。是输出四个空行啦。x操作把空的hold space和pattern space交换,然后空的pattern space又把它自己复制给hold space,这样hold space和pattern space永远都成空了。╮(╯▽╰)╭,解释清楚了没?

•接下去讲g/G,分别为复制(覆盖)/添加hold space到pattern space。
假如你想要把能匹配regex的这一行变成空行,可以考虑用g哦。假如你想要把能匹配regex的这一行下一行再添加一空行,可以考虑用G哦。
[smallpboy @local tmp]$ seq 4 | sed ‘/3/g’
1
2

4
原理?额,不用多说了吧。想不通?你再想想。额,很简单。我还是smallpboy啊,大鸟飞过就好了嘛。

sed其他:
1、        wc –l功能:
sed –n ‘$=’ filename     
“=”操作指打印行号,可以执行sed –n ‘=’ filename仔细体会下它和-n之间的关系。“$”指最后一行。
2、        tac 功能
sed ‘1!G;h;$!d’ filename
G和h操作还记得么?忘了就去前面看看吧。这里难懂的还有“1!”和“$!”,其实它俩很简单,在其他语言中就是“!1”和“!$”这样,即非第一行,非最后一行的意思。这块能懂吗?类似的,删除空行使用 sed ‘/^$/d’,那删除非空行呢?删除非空行?对,使用sed ‘/^$/!d’就好了嘛。好了,如果了解了“1!”和“$!”的意思了,现在可以开始分解上述命令了。
[smallpboy @local tmp]$ seq 3 | sed ‘1!;G’
1
2

3

“1”下面没有空行是否在你预料之内呢?G操作在第二行(也就是第二次操作)之后才起作用。
[smallpboy @local tmp]$ seq 3 | sed ‘1!;G;h’
这个就复杂了,h操作将pattern space内容复制到hold space,第一次读入1时,G操作被禁止,pattern space内容为1吧。然后h操作将这个1复制过去了。第二次读入2时,G操作将hold space内容添加到pattern space(结果为2\n1,能跟上我的思路不?),然后h操作将hold space的内容从第一次结束时的1,更新为2\n1,也就是第二次结束的结果。这样,当读入3时(也即是第三次),G操作将pattern space变成了3\n2\n1。是否?:)所以上述命令输出如下:
1
2\n1
3\n2\n1
能看懂么?当然,终端上可不会显示“\n”字符的哈。最后,指保留最后一行,大功告成:
[smallpboy @local tmp]$ seq 3 | sed ‘1!;G;h;$!d’
3
2
1
这样,全懂了吧? ╮(╯▽╰)╭,说的好纠结,好婆妈啊。^)^

3、        rev功能
sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1;//D;s/.//;’
实话说,这个命令确实,我也半懂不懂的,确实很复杂啊。勉强说下:
“/\n/!G;”这个能理解不?在stdin中没有找到\n,就主动添上\n。
“s/\(.\)\(.*\n\)/&\2\1;”中\(.\)先匹配一个字符,再匹配剩下所有字符。“&”指整个pattern space,\2和\1想必你也知道是什么东东。
“//D”指删除pattern space第一行,space非空就循环执行。“s/.//”就不解释了。假设我们有文件
[smallpboy @local tmp]$ cat file
123
file文件中“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….,发现匹配不了,此时pattern space没有被重新赋值。执行//D操作后,结果为321。然后,然后,就发现错了。。。。。
[smallpboy @local tmp]$ cat file | sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1;P;//D’  
123
23
3

321
#P print up to the first embedded newline of the current pattern space。
[smallpboy @local tmp]$ cat file | sed ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1;//D’

321

作者:良玉 分类:Shell 浏览:852 评论:0
留言列表
发表评论
来宾的头像