设为首页 收藏本站
查看: 872|回复: 0

【shell基础】05、sed进阶

[复制链接]

尚未签到

发表于 2018-8-23 13:37:43 | 显示全部楼层 |阅读模式
  一、多行模式:N;P;D
  上一篇sed使用基础入门中介绍的基础命令都是面向单行的,一般情况下,这种处理并没有什么问题,但是当匹配的内容是错开在两行时就会有问题,最明显的例子就是某些英文单词会被分成两行。
  幸运地是,sed允许将多行内容读取到模式空间,这样你就可以匹配跨越多行的内容。
  本篇笔记主要介绍这些命令,它们能够创建多行模式空间并且处理之。
  其中,N/D/P这三个多行命令分别对应于小写的n/d/p命令,后者我们在上一篇已经介绍。它们的功能是类似的,区别在于命令影响的内容不同
  例如:D命令与d命令同样是删除模式空间的内容,只不过d命令会删除模式空间中所有的内容,而D命令仅会删除多行模式空间中的第一行。
  1、N 读下一行
  我们学过n,将下一行的内容提前读入,并且将之前读入的行(在模式空间中的行,没有被编辑命令编辑)输出到屏幕,然后后续的命令会应用到新读入的行上。
  而N命令将下一行的内容读取到当前模式空间,但是和n不一样的地方是:N命令并没有直接输出当前模式空间中的行,而是把下一行追加到当前模式空间,两行之间用换行符\n连接
  例:要把test中“ssbb”替换成“你好”,我们发现ssbb跨越了2行,用sed之前的命令好像没有办法实现,现在用N命令试试
[root@localhost tmp]# cat test  
aaa aaaaaa aaaa ss
  
bb ccccc cccccc ccc
  
ds sdfas  centos rhess
  
bbntoo cobbler mint
  
[root@localhost tmp]# sed 'N;s/ss\nbb/nihao/' test
  
aaa aaaaaa aaaa nihao ccccc cccccc ccc
  
ds sdfas  centos rhenihaontoo cobbler mint
  
[root@localhost tmp]# sed '1N;s/ss\nbb/nihao/' test
  
aaa aaaaaa aaaa nihao ccccc cccccc ccc
  
ds sdfas  centos rhess
  
bbntoo cobbler mint
  可以看出N将匹配到的行的下行通过\n追加在匹配到的行的后面变成了一行,再使用s替换就可以了
  不过这个例子有两个局限:
  我们知道ssbb分割的位置;
  执行替换命令后,前后两行拼接在一起,导致这行过长;
  第二点,可以这样解决:
[root@localhost tmp]# sed 'N;s/ss\nbb/nihao\n/' test  
aaa aaaaaa aaaa nihao
  
ccccc cccccc ccc
  
ds sdfas  centos rhenihao
  
ntoo cobbler mint
  
[root@localhost tmp]# sed 'N;s/ss\nbb/\nnihao/' test
  
aaa aaaaaa aaaa
  
nihao ccccc cccccc ccc
  
ds sdfas  centos rhe
  
nihaontoo cobbler mint
  2、D  删除行
  该命令删除多行模式空间中的首行,把连接符\n也删了,而它对应的小d命令删除模式空间的所有内容。D不会影响到读入新行,相反它会回到最初的编辑命令,重复应用在模式空间剩余的内容上。
  后面半句开始比较难以理解,用一个例子来解释下。现在我们有一个文本文件,内容如下所示,行之间有空行:
[root@Note3 src]# cat N.txt  
This line is followed by 1 blank line.
  

  
This line is followed by 2 blank lines.
  

  

  
This line is followed by 3 blank lines.
  

  

  

  
This line is followed by 4 blank lines.
  

  

  

  

  
This is the end.
  现在我们要删除多余的空行,将多个空行缩减成一个空行,怎么实现?
  是不是只有用刚学的N命令好处理
[root@Note3 src]# sed '/^$/{N;/^\n$/d}' N.txt  
This line is followed by 1 blank line.
  

  
This line is followed by 2 blank lines.
  
This line is followed by 3 blank lines.
  

  
This line is followed by 4 blank lines.
  
This is the end.
  但是有没有发现连在一起的空行总数是偶数就都被删了,而奇数的相连空行已经被合并成一行
  造成这样的原因需要重新看下上面的命令,当匹配一个空行,是将下一行也读取到模式空间,然后若下一行也是空行,则模式空间中的内容应该是\n,因此匹配^\n$,从而执行d命令会将模式空间中的内容清空,结果就是相连的两个空行都被删除。这样就可以理解为什么相连奇数个空行的情况下是正常的,而偶数个数就有问题了。
  这种情况下,我们就应该用D命令来处理,这样做就得到预期的结果了:
[root@localhost tmp]# sed '/^$/{N;/^\n$/D}' test1  
This line is followed by 1 blank line.
  

  
This line is followed by 2 blank lines.
  

  
This line is followed by 3 blank lines.
  

  
This line is followed by 4 blank lines.
  

  
This is the end.
  D命令只会删除模式空间的第一行,而且删除后会重新在模式空间的内容上执行编辑命令,类似形成一个循环,前提是相连的都是空行。当匹配一个空行时,N读取下一行内容,此时匹配^\n$导致模式空间中的第一行被删除。现在模式空间中的内容是空的,重新执行编辑命令,此时匹配/^$/。继续读取下一行,当下一行依然为空行时,重复之前的动作,否则输出当前模式空间的内容。造成的结果是连续多个空行,只有最后一个空行是保留输出的,其余的都被删除了。这样的结果才是我们最初希望得到的。
  3、P 打印行
  P命令与p命令一样是打印模式空间的内容,不同的是前者仅打印模式空间的第一行内容,而后者是打印所有的内容。因为编辑命令全部执行完之后,sed默认会输出模式空间的内容,所以一般情况下,p和P命令都是与-n选项一起使用的。但是有一种情况是例外的,即编辑命令的执行流程被改变的情况,例如N,D等。很多情况下,P命令都是用在N命令之后,D命令之前的。这三个命令合起来,可以形成输入输出的循环,并且每次只打印一行:读入一行后,N继续读下一行,P命令打印第一行,D命令删除第一行,执行流程回到最开始重复该过程:
[root@Node5 src]# echo -e "line1\nline2\nline3"  
line1
  
line2
  
line3
  
[root@Node5 src]# echo -e "line1\nline2\nline3"|sed 'N;P;D'
  
line1
  
line2
  
line3
  不过多行命令用起来要格外小心,你要用自己的大脑去演算一遍执行的过程,要不然很容易出错,
  比如:
[root@Node5 src]# echo -e "line1\nline2\nline3" | sed -n 'N;1P'  
[root@Node5 src]# echo -e "line1\nline2\nline3" | sed -n 'N;P'
  
line1
  
[root@Node5 src]# echo -e "line1\nline2\nline3" | sed -n 'N;2P'
  
line1
  你可能期望打印第一行的内容,事实上并没有输出。原因是当N继续读入第二行后,当前行号已经是2了,行号只是sed在内部维护的一个计数变量而已,每当读入新的一行,行号就加1:
[root@localhost tmp]# echo -e "line1\nline2\nline3" | sed '$!N;='  
                   # $!N表示如果是最后一行则不执行N命令了
  
2
  
line1
  
line2
  
3
  
line3
  
[root@localhost tmp]# echo -e "line1\nline2\nline3" | sed -n '$!N;='
  
2
  
3
  二、保持空间
  1、保持空间简介
  上面N;P;D三个命令,它们可以形成多行的模式空间,在一点程度上弥补了单行模式空间的不足,我们用sed编辑文本的能力又进了一步。模式空间是sed内部维护的一个缓存空间,它存放着读入的一行或者多行内容。但是模式空间的一个限制是无法保存模式空间中被处理的行,因此sed又引入了另外一个缓存空间——保持空间(Hold Space)。
  保持空间:
  保持空间用于保存模式空间的内容,模式空间的内容可以复制到保持空间,同样地保持空间的内容可以复制回模式空间。sed提供了几组命令用来完成复制的工作,其它命令无法匹配也不能修改保持空间的内容。
  操作保持空间的命令如下所示:
  名称          命令                     命令说明
  保存(Hold)         h/H          将模式空间的内容覆盖或者追加到保持空间
  取回(Get)         g/G          将保持空间的内容覆盖或者追加到模式空间
  交换(Exchange)      x           交换模式空间和保持空间的内容
  这几组命令提供了保存、取回以及交换三个动作,交换命令比较容易理解,保存命令和取回命令都有大写和小写两种形式,这两种形式的区别是小写的是将会覆盖目的空间的内容,而大写的是将内容追加到目的空间,追加的内容和原有的内容是以\n 连接。
  2、基本使用
  我们随便试试这几个命令,假设有如下测试文本:
[root@Node5 src]# cat test2  
1
  
2
  
11
  
22
  
111
  
222
  首先,仅使用h/H或者g/G命令:
[root@Node5 src]# sed 'h' test2     # 将模式空间的内容覆盖到保持空间,打印模式空间的内容  
1
  
2
  
11
  
22
  
111
  
222
  
[root@Node5 src]# sed 'H' test2    # 将模式空间的内容覆盖到保持空间,打印模式空间的内容
  
1
  
2
  
11
  
22
  
111
  
222
  

  
[root@Node5 src]# sed 'g' test2   # 将保持空间的内容覆盖到模式空间,打印模式空间的内容
  

  

  

  

  

  

  

  
[root@Node5 src]# sed 'G' test2  # 将保持空间的内容追加到模式空间,打印模式空间的内容
  
1
  

  
2
  

  
11
  

  
22
  

  
111
  

  
222
  

  
[root@Node5 src]#
  前者返回的结果正常,因为复制到保持空间的内容并没有取回;后者每一行的后面都多了一个空行,原因是每行都会从保持空间取回一行,追加(大写的G)到模式空间的内容之后,以\n分隔。
  这个很好理解吧,理解了就没什么问题了。
  使用x命令交换空间:
[root@Node5 src]# sed 'x' test2  

  
1
  
2
  
11
  
22
  
111
  
[root@Node5 src]#
  命令执行后,发现前面多了一个空行并且最后一行不见了。
  想sed命令用的好,需要脑补命令执行过程:
  * 当读入第一行的时候,模式空间中的内容是第一行的内容,而保持空间是空的,这个时候交换两个空间,导致模式空间为空,保持空间为第一行的内容,因此输出为空行;
  * 当读入下一行之后,模式空间为第2行的内容,保持空间为第一行的内容,交换后输出第1行的内容;
  * 依次读入每一行,输出上一行的内容;
  * 直到最后一行被读入到模式空间,交换后输出倒数第二行的内容,而最后一行的内容并没有输出,此时命令执行结束。
  3、深入使用
  看了上面的内容是不是觉得sed的高级用法也不过如此?
  上面的例子简单地介绍了保持空间命令的基本使用方法,这些命令单个使用可能效果不大,但是组合起来的效果是非常好的。
  第一个例子:使用逗号拼接行
[root@localhost src]# sed 'H;$!d;x;s/^\n//;s/\n/,/g' test2  
1,2,11,22,111,222
  是不是瞬间高上大了,装x利器啊!
  其实理解了上面说对保持空间的说明,这里就很容易理解了,上面的命令执行过程是这样的:
  使用H将每一行都追加到保持空间,这里利用d命令打断常规的命令执行流程,让sed继续读入新的一行,直接到将最后一行都放到保持空间。这个时候使用x命令将保持空间的内容交换到模式空间,模式空间的内容现在是这样的“\n1\n2\n11\n22\n111\n222”替换的步骤分成两个,首先去掉首个回车符,然后把剩余的回车符替换成逗号。
  其它例子:
  sed 'G'                      在文件中的每行后方添加空白行;
  sed '$!d'                     保留最后一行;
  sed '/^$/d;G'                  保证指定的文件每一行后方有且只有一个空白行;
  sed 'n;d'                     保留奇数行;
  sed -n 'p;n'                   保留奇数行
  sed -n 'n;p'                   保留偶数行
  sed 'N;s/\(.*\)\n\(.*\)/\2\n\1/'      奇数行和偶数行调换
  sed -n 'h;n;G;p'                奇数行和偶数行调换
  sed -n '1!G;h;$p'                倒行输出原文内容
[root@Node5 src]# sed -n '1!G;h;$p' test2  
222
  
111
  
22
  
11
  
2
  
1
  读取第一行:
  1!G:不符合地址定界不操作;
  h:把模式空间的行原文num1复制并覆盖到保持空间;
  $p:不符合地址定界,不操作;
  读取第二行:
  1!G:将取保持空间的行原文num1通过\n与读取进来的num2连接;
  h:把模式空间的行(num1+\n+num2)复制并覆盖到保持空间;
  $p:不符合地址定界,不操作;最后模式空间只有原文num2+\n+num1,
  读取最后一行:
  1!G:模式空间变成num3+\n+num2+\n+\num1;
  h:保持空间变成num3+\n+\num2+\num1;
  $p:打印模式空间的num3+\n+num2+\num1;
  最后只有打印这个操作,所以输出num3+\n+num2+\n+num1
  sed '$!N;$!D'               删除了第一行
  读取第一行:!N:num1+\n+num2;!$D:num2;最后输出模式空间的num2
  读取第三行:$!N:不符合地址定界不操作,num2;$!D:不符合地址定界,不操作,num2;最后输出num2
  4、流程控制
  一般情况下,sed是将编辑命令从上到下依次应用到读入的行上,但是像d/n/D/N命令都能够在一定程度上改变默认的执行流程,甚至利用N/D/P三个命令可以形成一个强大的循环处理流程。除此之外,其实sed还提供了分支命令(b)和测试(test)两个命令来控制流程,这两个命令可以跳转到指定的标签(label)位置继续执行命令。
  标签是以冒号开头的标记,如下例中的:top标签:
  :top
  command1
  command2/pattern/b top
  command3
  当执行到/pattern/b top时,如果匹配pattern,则跳转到:top标签所在的位置,继续执行下一个命令command1,如果没有指定标签,则将控制转移到脚本的结尾处,这是一个默认的行为,有时候如果用得好也是非常有用的,例如:
  /pattern/b
  command 1
  command 2
  command 3
  当执行到/pattern/b时,如果匹配pattern,则跳转到最后。这种情况下匹配pattern的行可以避开执行后续的命令,被排除在外。
  下一个例子中,我们利用分支命令的跳转效果达到类似if语句的效果:
  command1/pattern/b end
  command2:end
  command3
  当执行到/pattern/b end时,如果匹配pattern,则跳转到:end标签所在的位置,跳过command2而不执行。
  进一步地,利用两个分支命令可以达到if..else的分支效果:
  command1/pattern/b dothree
  command2
  b:dothree
  command3
  这个例子中,当执行到/pattern/b dothree时,若匹配pattern则中转到:dothree标
  签,此时执行command3;若不匹配,则执行command2,并且跳转到最后。
  例子:
[root@localhost tmp]# echo "a a a a a "|sed ' /[a-z]/ s//A/;/[a-zA-Z]/ s//b/'  
A b a a a
  
[root@localhost tmp]# echo "a a a a a "|sed ' /[a-z]/s//A/;b;/[a-z]/s//b/'
  
A a a a a
  
[root@localhost tmp]# echo "a a a a a "|sed ':top /[a-z]/s//A/;/[a-z]/b;/[a-z]/s//b/'
  
A a a a a
  
[root@localhost tmp]# echo "a a a a a "|sed ':top /[a-z]/s//A/;/[a-z]/b top;/[a-z]/s//b/'
  
A A A A A
  
[root@localhost tmp]# echo "a a a a a "|sed ' /[a-z]/s//A/;b top;/[a-
  
z]/s//b/;:top;p'
  
A a a a a
  
A a a a a
  上面的例子都是用到了分支命令,b分支命令的跳转是无条件的。而与之相对的是t测试命令,测试命令的跳转是有条件的,当且仅当当前行发生成功的替换时才跳转。
[root@localhost tmp]# echo "a a a a a "|sed ' /[a-z]/s//A/;t;/[a-z]/s//b/'  
A a a a a
  
[root@localhost tmp]# echo "a a a a a "|sed ' /[a-z]/s//A/;/[0-9]/t;/[a-z]/s//b/'
  
A b a a a



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-555596-1-1.html 上篇帖子: 一些现学现用的shell-Susir驿站 下篇帖子: shell编程系列之awk用法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表