0755mx 发表于 2018-8-21 09:09:52

【shell基础】05、sed入门详解

  一、sed概述
  文件:本质是数据流,被标记的一断硬盘上的存储空间,存储在数据块中的字节流,文件名可以引用这段数据流
  sed是一种非交互式的流编辑器,通过多种转换修改流经它的文本
  sed会一次处理一行内容;处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。默认情况下文件内容并没有改变(可以添加-i选项修改原文件)
  另外需要注意的是,和grep不同,sed不论是否找到指定的模式,它的退出状态都是0(Linux中通常代表真)。只有存在语法错误时,sed的退出状态才不是0。
  sed的处理流程,简化后是这样的:
  1、读取文本第一行内容到模式空间;
  2、如果不符合地址定界,则忽略后续的命令,并把模式空间的内容送往屏幕,继续读取下一行;
  3、如果符合地址定界,则执行后续的命令,并把模式空间的内容送往屏幕,继续读取下一行,一直重复2,3步骤直到全部行读取完毕
  注意:不管是否符合地址定界都会进入模式空间,只要经过编辑命令后还在模式空间的行都默认会输出到屏幕
  二、sed的使用
  sed "SCRIPTS" FILENANE...
  常用用法:
  sed 'addr1[,add2]编辑命令' FILENAME...
  sed 'addr1[,addr2]s/查找条件/替换文本/修饰符' FILENAME...
  #如果不定界默认匹配全文
  选项:
  -n:静默模式,不输出模式空间中的内容,但会输出编辑命令处理后的内容
  -r:支持扩展的正则表达式
  -f:/PATH/TO/sed_script_file:指定sed的脚本文件,文件中的脚本不需要引号
  -e: 用法: -e‘SCRIPT’-e 'SCRIPT /FILENAME   指定多个编辑指令,“;”也可
  -i:把操作保存原文件
  编辑命令:
  d:删除    删除命令会将模式空间中的内容全部删除,并且导致后续的编辑命令不会执行并且读入新行,因为当前模式空间的内容已经为空。
  p:打印       定界查找到的行,打印出来(和默认输出模式空间的行就打印2次了)
  a [\]string    在某行之后插入行,添加多行时,除最后一行外,string中可以\n表示换行,这样来实现添加多行;字符串前面\可以不写,但建议写,增加可读性
  c [\]string    替换某行,.....
  i [\]string    在某行之前插入行,......
  r /FILENAME    在指定行号插入某文件的内容
  w /FILENAME    将符合条件的所有行保存至指定的文件中,覆盖添加
  =    显示符合条件的行的行号,显示在该行的前一行行首
  l   (是小写字母l不是数字1)命令类似p命令,不过会显示控制字符
  s///    和vim中替换的用法一样:查找条件可以使用模式,但要替换为的内容不可以,但可以使用后向引用\1,\2,&,也可以使用“@”或“#”替换“/”,
  y/SET1/SET2/    转换字符;将SET1中出现的字符替换成SET2中对应位置的字符
  n    将下一行的内容提前读入,并且将之前读入的行(在模式空间中的行,没有被编辑命令编辑)输出到屏幕,然后后续的命令会应用到新读入的行上。
  地址定界:
  startline,endline    例:1,3第一行到第三行
  /pat1/,/pat2/       被第一个模式匹配到的行开始到第二个模式匹配行结束
  /partern/          被模式匹配到的行
  startline,/parttern/还可以混合使用,但/partten/,endline不可以
  地址定界需要注意:
  1、对于像“addr1,addr2”这种形式的地址匹配,如果addr1匹配,则匹配成功,“开关”打开,在该行上执行命令,此时不管addr2是否匹配,即使addr2在addr1这一行之前;
  接下来读入下一行,看addr2是否匹配,如果addr2在addr1之前,则不匹配,不执行命令,关闭“开关”;如果addr2在addr1之后,则一直处理到匹配为止,换句话说,如果addr2一直不匹配,则开关一直不关闭,因此会持续执行命令到最后一行。
  2、对于0,/partter/这种形式默认第一个地址是匹配的,然后直到第一个被/parttern/匹配到的行为止。因此上述情况,只要看每一行是否匹配第二个地址就可以了addr2,因为第一行是匹配的,
  如果/partter/匹配不到则addr2(没有/partter/匹配的行),则匹配后面的所有行
  但是这种地址对表示有一个限制,即addr2只能使用/parttern/形式,如果使用行号,就会出错,不信可以试试。
  3、地址对addr1, addr2的匹配方式 ,从匹配addr1的那行开始,打开匹配开关,直到匹配addr2的那行结束,关闭匹配开关,之后的行会忽略这个地址对,不再做匹配。
  如果addr1是number,即行号,如果新读入行的行号大于addr1,则匹配;小于addr1,则不匹配。
  如果addr2是行号,如果新读入行的行号小于addr2,则匹配,继续往下读;大于addr2,则不匹配,关闭匹配开关。
  4、如果地址或者地址对之后有一个"!",表明对匹配的行不执行后面的命令,刚好相反。
  
  再补充总结s替换时所用到的编辑命令:
  通用格式为:s/oldstring/newstring/flag
  oldstring   要被替换的内容,支持正则表达式
  newstring   新内容,不支持正则,但支持old中分组的后向引用,还有下面几个特殊用法\1,\2... 对old中\(\)分组内容进行引用,&引用整个old中的值
  下面的不常用:
  \U 把\U后面的字母全部转换为大写
  \u 只把\u后面的第一个字母大写,其他不变
  \L 把\L后面的字母全转换为小写
  \l 只把\l后的第一个字母小写,其他不变
  \E 代表终止,后面的不再处理
# sed -n 's/^a/\Usb/p' passwd   #所以有毛用,不用记这些命令  
SBroot:x:0:0:root,,,a:/root:/bin/bash
  
SBda:x:3:4:adm:/var/adm:/sbin/nologin
  
SB:x:3:4:adm:/var/adm:/sbin/nologin
  flag(修饰符)实现增强功能模块的,默认情况只会替换每行首个匹配的内容
  g全局替换,行中所有被匹配的都会被替换
  i不区分字符大小写
  p打印模式空间的内容,请注意通常与sed命令选项-n配合使用只打印处理行
  wfile 将模式空间行打印写到file中
  N每行中被模式匹配的第N个内容被替换
  l命令类似p命令,不过会显示控制字符,这个命令和vim的list命令相似,例如:
# echo "column1 column2 column3" | sed 'l'  
column1 column2 column3$
  
column1 column2 column3
  
# echo "column1 column2 column3" | sed '='
  
1
  
column1 column2 column3
  
# echo "column1 column2 column3" | sed 'p'
  
column1 column2 column3
  
column1 column2 column3
  
# echo "column1 column2 column3" | sed 'l'
  
column1 column2 column3$
  
column1 column2 column3
  如果flags中明确指定替换第n次的匹配,例如N=2:
# echo "column1 column2 column3 column4" | sed 's/ /;/2'  
column1 column2;column3 column4
  当替换命令的pattern与地址部分是一样的时候,比如/regexp/s/regexp/replacement/可以省略替换命令中的pattern部分,这在单个编辑命令的情况下没多大用处,但是在组合命令的场景下还是能省不少功夫的
  例如:把文中aa都加上s然后在有aa的行的行首再加上+
# cat aa.txt  
aabb   cc
  
daadsa   casdfa
  
dasddasfaa
  
bb   cc   nihao
  

  
# sed '/aa/ s//aas/g;s/^/+/' aa.txt
  
+aasbb   cc
  
+daadsa   casdfa
  
+dasddasfaas
  
+bb   cc   nihao
  
# sed '/aa/ {s//aas/g;s/^/+/'} aa.txt
  
+aasbb   cc
  
daadsa   casdfa
  
+dasddasfaas
  
bb   cc   nihao
  
# sed -e '/aa/ s//aas/g' -e '/aa/ s/^/+/' aa.txt
  
+aasbb   cc
  
daadsa   casdfa
  
+dasddasfaas
  
bb   cc   nihao
转换命令: y
  语法:
y/SET1/SET2/  它的作用是在匹配的行上,将SET1中出现的字符替换成SET2中对应位置的字符,
  例如1,3y/abc/xyz/会将1到3行中出现的a替换成x,b替换成y,c替换成z。是不是觉得这个功能很熟悉,其实这一点和tr命令是一样的。可以通过y命令将小写字符替换成大写字符,不过命令比较长:
$ echo "hello, world" | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'HELLO, WORLD  使用tr命令来转换成大写:
$ echo "hello, world" | tr a-z A-Z  HELLO, WORLD
取下一行命令: n
  语法:
n  n命令为将下一行的内容提前读入,并且将之前读入的行(在模式空间中的行)输出到屏幕,然后后续的命令会应用到新读入的行上。因此n命令也会同d命令一样改变sed的控制流程。
  书中给出了一个例子来介绍n的用法,假设有这么一个文本:
$ cat text  .H1 "On Egypt"
  Napoleon, pointing to the Pyramids, said to his troops:
  "Soldiers, forty centuries have their eyes upon you."
  现在要将.H1后面的空行删除:
$ sed '/.H1/{n;/^$/d}' text  .H1"On Egypt"
  Napoleon, pointing to the Pyramids, said to his troops:
  "Soldiers, forty centuries have their eyes upon you."
  退出命令: q
  语法
q    #不能使用addr1,addr2,只能使用某行,读取到该行就退出  当sed读取到匹配的行之后即退出,不会再读入新的行,并且将当前模式空间的内容输出到屏幕。
  例如打印前3行内容:
# cat test  
aabb   cc
  
daadsa   casdfa
  
dasddasfaa
  
bb   cc   nihao
  

  
# sed '3q' test
  
aabb   cc
  
daadsa   casdfa
  
dasddasfaa
  
# sed -n '3p' test
  
dasddasfaa
  
# sed -n '1,3p' test
  
aabb   cc
  
daadsa   casdfa
  
dasddasfaa
  打印前3行也可以用p命令:但是对于大文件来说,前者比后者效率更高,因为前者读取到第N行之后就退出了。后者虽然打印了前N行,但是后续的行还是要继续读入,只不会不作处理。
  到此为止,sed基础命令的部分就介绍完了。
  示例(需要注意的细节):
  1、让我们来看一个非常简单的例子,将一段文本中的pig替换成cow,并且将cow替换成horse:
# echo "pig a cow"|sed 's/pig/cow/g;s/cow/hores/g'  
hores a hores
  初看起来好像没有问题,但是实际上是错误的,原因是第一个替换命令将所有的pig都替换成cow,紧接着的替换命令是基于前一个结果处理的,将所有的cow都替换成horse,造成的结果是全部的pig/cow都被替换成了horse,这样违背了我们的初衷。
  在这种情况下,只需要调换下两个编辑命令的顺序:
  # echo "ping a cow"|sed 's/cow/hoers/;s/ping/cow/'
  2、分析下面2个命令的执行结果,为什么会是这样?
  seq 6|sed '1,2d'|sed '1,2d'
  seq 6|sed -e '1,2d' -e '1,2d'
  第一眼觉得结果都是5,6
  想了下第一个结果是5,6,第2个结果是3,4,5,6
  不猜了,在电脑上分别执行一下这2个命令,出人意料
  解释:
  首先第一行被读入,遇到第一组expression -> 1, 2d,第一行匹配成功(打开匹配开关),执行d命令,d命令清空模式空间的内容,因此不会再执行接下来的命令。
  继续从标准输入读入第二行,同第一行,读入第三行,第一组expression匹配失败(因为3>2),因此试着执行第二组expersson->1,2d,因为3>1,打开匹配开关,执行d。(这里是关键)
  读入第四行,执行第二组expersson->1,2d,因为4>2,匹配失败,关闭匹配开关,同时也不执行d。
  因此,最后第1 2 3行被删除。
  练习:
  He like his liker
  He like his lover
  she love her liker
  she love her lover
  1、删除以上内容当中包含“l..e”前后一致的行
  # sed "/\(l..e\).\+\1/d"
  2、将文件中“l..e”前后一致的行中,最后一个"1..e"词首的l换成大写L
  # sed "/\(l..e\).\+\1/ s/l\(..er\)/L\1/"
  # sed "/\(l..e\).\+\1/ s/l\(..er\)/L\1/p"      #替换时修饰符的位置还可以用p和w,打印或保存更改后的内容
  # sed "/\(l..e\).\+\1/ s/l\(..er\)/L\1/w /tmp/22222"
  3、替换/etc/inittab文件中"id:3:initdefault:"-行中的数字为5
  # sed "s/\(id\)::\(initdefault:\)/\1:5:\2/p"
  4、去取一个文件路径的目录名称,如/etc/sysconfig/network其目录为/etc/sysconfig,功能同dirname
  # echo "/etc/sysconfig/network"|sed 's@/[^/]\+/\?$@@'#不知道为什么这里用""号就会报错,以后记得尽量用'号了,单引号更通用(当然‘’内不使用变量)


页: [1]
查看完整版本: 【shell基础】05、sed入门详解