注:本文除特殊注明外均针对传统UNIX中的sed,而非GNU的版本,以保证通用性,GNU的新特性本文暂不进行介绍,请参看手册。另外,本文对sed的讲述和总结不为求全面,只求实用性和适用性强。有需要还是参阅man手册和sed相关资料。
1.原理篇
掌握这个东西首先需要掌握的就是原理,否则一切技巧都是白扯。sed以行为处理单位,默认输入输出均为系统标准输入输出(因此除非重定向,否则它并不真正修改文件),它首先判断要处理的行是否在要处理的范围之内(下一章中称之为SELECTION),如果是则读入pattern space中,这是sed进行字符串处理工作的一个区域。脚本中的sed命令逐条执行来编辑pattern space里面的字符串,执行完毕后将该pattern space中处理过的字符串进行输出,随之pattern space被清空;接着,再重复执行刚才的动作,文件中的新的一行被读入,判断是否在SELECTION中,编辑、输出,直到文件处理完毕,整个过程如下图所示。除了 pattern space 外,sed还有一个 hold space,用处是暂存文字字符串的地方,hold space中的字符串只是用于临时处理的中间结果,是不会被输出的(在本文第四章会有介绍,此时不了解不影响阅读此文)。
2.用途篇
学习一个脚本语言,了解了基本原理后就要调研一下这个东东是不是满足完成你任务的需求。一提到sed,肯定就会牵扯到awk,它具体的功能会在后边的awk篇进行叙述。对于同一个任务,sed和awk都有可能解决,因此对于sed、awk的用途每个人都有不同的习惯和自己擅长的用法,在实践中,我个人习惯于用sed进行行处理,也就是根据sed的原理,需要进行一行行的处理操作时优先使用sed。而用awk更多的进行列处理。而在具体任务上,由于sed强大的替换能力和编辑能力,我常常用sed作为编辑器,而awk 作为信息获取和格式处理输出的能力。#p#
3.初级语法篇
作为一个脚本类工具,确定就要用它完成任务后,要开始真正使用它,掌握其语言特性是必不可少的。形式上,使用sed采用如下命令格式:
sed [options] 'SELECTION edit-instructions' file(s)
从命令格式可以看出sed可以一次处理多个文本。此处先不介绍options,因为options往往要和后边的command进行配合而才能体现出价值。下边,我们先从command开始了解。
根据是否使用hold space(不懂这个概念也可先往下看,读完本文就明白)的区别,对于一个初级用户,了解如下sed中不使用hlod space 的command是实践的第一步。本文在此基础上以解决问题的思路首先来介绍不使用hold space 的命令,另外需要说明的是正则表达式是另一个配合使用的利器,在本文中为了不引入更大的麻烦,因此用例中尽量使用最简答的正则表达式。
1)范围选取
在sed中如果不指定范围,则处理命令是针对整个标准输入的。如需要在某个范围内进行处理,则需要进行范围选取。也就是命令格式中的SELSECTION。sed根据SELECTION取得相应的文本行,在这些行中根据edit-instructions进行编辑。注意,SELECTION和edit-instructions中的空格不是必须的。
SELECTION 可以如下表达:
- 单个行号:如1为取第一行,5为取第五行,$为取最后一行
- 行范围:如5,$ 为取从第五行到最后一行之间的文本行
- 单个正则匹配:如/string/ 为取包含string的行
- 一个正则匹配范围:如/^on/,/off$/ 为取从on开头的行到off结尾的行之间(含这两个匹配行)所包含的文本行。
- 行范围与正则匹配范围集合:如10,/man/表示从第10行到包含有man的行之间的文本
- 除去所匹配行外的范围:如/Llew/! 表示除了匹配Llew的行外其余的文本行
在进入处理命令前,先介绍一下本文中的几个示例文件
phonelist:
JCHJCL01:/tmp/gnuhpc#cat phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
paths:
JCHJCL01:/tmp/gnuhpc#cat paths /opt/virtprovider/lib /var/adm/syslog /usr/bin/ /usr/local/bin
config.ini:
JCHJCL01:/tmp/gnuhpc#cat config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
2)打印命令
明确如何界定需要处理文本后,首先学习一个简单的命令:打印这部分文本。
基本语法:SELSECTIONp 打印pattern space中的内容
Task1:打印包含Franklin的行
JCHJCL01:/tmp/gnuhpc#sed -n '/Franklin/p' phonelist Franklin, Francis 704-3876
注意,选项-n 表示所有都不打印,而仅仅打印出匹配的行,可以试一试没有这个选项的情况。回顾sed机制,它会将文本一行行放在pattern space,不管你做什么样的后续操作、甚至不做任何编辑动作,它都会在command执行完后把pattern space打出来,这你就理解了为什么要用这个选项。
3)处理命令
a) 增改操作:
基本语法:
SELECTIONx\ text 其中斜杠后有回车,而x则为: i 表示插入选中行前 a 表示追加在选中行之后 c 表示将选中行修改为text
Task2:在第二行前插入一个联系人Jonney, Wang 923-3322
JCHJCL01:/tmp/gnuhpc#sed '2i\ > Jonney, Wang 923-3322' phonelist Terrell, Terry 617-7989 Jonney, Wang 923-3322 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task3:在Martin, Marty后加入联系人Jonney, Wang 923-3322
JCHJCL01:/tmp/gnuhpc#sed '/Martin, Marty/a\ > Jonney, Wang 923-3322' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Jonney, Wang 923-3322 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task3:将名字为Llewellyn的记录都记为“BANNED”
JCHJCL01:/tmp/gnuhpc#sed '/Llewellyn/c\ BANNED' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 BANNED Jansen, Jan 903-3333 BANNED
b)删除操作
基本语法:
SELECTIONd ,清除pattern space中的所有内容
Task4 删除最后一行:
JCHJCL01:/tmp/gnuhpc#sed '$d' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333
c)替换操作:
基本语法:
'SELECTION s/old string/new string/’ 替换所选区域中第一次出现的old string 'SELECTION s/old string/new string/g’ 替换所选区域中所有的old string 'SELECTION y/string1/string2/’ 对所选区域中的string1所含字符对应替换为string2中同位置的字符,与tr命令相同。
Task5:将第一个Robin替换为Robbins
JCHJCL01:/tmp/gnuhpc#sed 's/Robin/Robbins/' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robbinsson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task6:将所有Rob替换为John
JCHJCL01:/tmp/gnuhpc#sed 's/Rob/John/g' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Johninson, Johnin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task7:将/usr/bin/中的/bin/替换为/bin
JCHJCL01:/tmp/gnuhpc#sed 's/\/bin\//\/bin/' paths /opt/virtprovider/lib /var/adm/syslog /usr/bin /usr/local/bin
在这种出现很多/的文件时需要\来进行转义,稍微一多就容易出错,那么采用如下的方式把替换分隔符的方式进行就好,其中感叹号只是一个其他类字符,换做另外一个字符(例如@)也是没有关系的:
JCHJCL01:/tmp/gnuhpc#sed 's!/bin/!/bin!' paths /opt/virtprovider/lib /var/adm/syslog /usr/bin /usr/local/bin
Task8:加密所有的1234,规则为将文件中1、2、3、4对应改为A、B、C、D:
JCHJCL01:/tmp/gnuhpc#sed 'y/1234/ABCD/' phonelist Terrell, Terry 6A7-7989 Franklin, Francis 70D-C876 Patterson, Pat 6AD-6ABB Robinson, Robin DAA-C7D5 Christopher, Chris C05-598A Martin, Marty 8AD-5587 Llewellyn, Lynn CA6-6BBA Jansen, Jan 90C-CCCC Llewellyn, Lee 8A7-88BC
d)写文件操作:
基本语法:'SELECTION command/w filename’
Task9:将所有Rob 改为Robbin,并将结果写到一个叫做result 的文件中
JCHJCL01:/tmp/gnuhpc#sed 's/Rob/Robbin/gw result' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robbininson, Robbinin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823 JCHJCL01:/tmp/gnuhpc#cat result Robbininson, Robbinin 411-3745
e)读文件操作:
基本语法:'SELECTION command/r filename’
Task10:如果phonelist中存在Patterson,则将文件paths的内容加入到Patterson后的那一行
JCHJCL01:/tmp/gnuhpc#sed '/Patterson/r paths' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 /opt/virtprovider/lib /var/adm/syslog /usr/bin/ /usr/local/bin Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
f)批处理操作:
如果要处理的很多,我们也可以将sed命令写入一个脚本,然后运行时采用-f选项指定运行该脚本就行。请注意sed会将第一条命令执行的结果发给第二条执行,因此命令的顺序尤为重要。
基本语法:sed -f scriptfile filename
Task11:把617替换为817,把704替换为522,把411替换为235
JCHJCL01:/tmp/gnuhpc#cat subscript s/617/817/ s/704/522/ s/411/235/ JCHJCL01:/tmp/gnuhpc#sed -f subscript phonelist Terrell, Terry 817-7989 Franklin, Francis 522-3876 Patterson, Pat 614-6122 Robinson, Robin 235-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
也可以使用多行操作模式,基本语法为:
'SELECTION1 operation1 … SELECTIONn operationn'
其实就是把多个命令用回车连起来
Task12:将Martin替换Mary,将Tearrey替换为Tearrey
JCHJCL01:/tmp/gnuhpc#sed 's/Martin/Mary/ s/Terrell/Tearrey/' phonelist Tearrey, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Mary, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
另一种方式是使用-e选项,基本语法为:
-e ‘command1’ –e ‘command2’
同样的任务:
JCHJCL01:/tmp/gnuhpc#sed -e 's/Martin/Mary/' -e 's/Terrell/Tearrey/' phonelist Tearrey, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Mary, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
还有另外一种方法,有点类似于C语言中分号的使用:
JCHJCL01:/tmp/gnuhpc#sed 's/Martin/Mary/;s/Terrell/Tearrey/' phonelist Tearrey, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Mary, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
我一般习惯于采用分号,简单也够明了。另外,对于同一个区域,还可以使用{}进行处理:
Task13:将含有QM的行中QM改为PM,=号改为“:”
JCHJCL01:/tmp/gnuhpc#sed '/QMPath/{s/QM/PM/ > s/=/:/ > }' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 PMPath: [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
#p#
4.进阶语法篇之hold space的使用
开篇提到了这个hold space,再复习一遍:Hold space 是 sed 用来暂存 pattern space 内容的一个临时空间。在处理中,有时我们希望保留pattern space的内容在下一次进行处理,因此sed的开发者设计实现了hold space,并且提供了很多命令在pattern space和hold space之间进行复制。记忆上,g和G都是get 的意思,表示从hold space取出放回pattern space,而h和H都是hold的意思,也就是从pattern space到hlod space。
基本语法:
g :将hold space中的内容拷贝到pattern space中,原来pattern space里的内容清除 G:pattern space末尾加上换行符后将hold space中的内容append到pattern space中 h:将pattern space中的内容拷贝到hold space中,原来的hold space里的内容被清除 H:hold space末尾加上换行符后将pattern space中的内容append到hold space中 x :交换 hold space 与 pattern space 内容
Task13:倒置phonelist
我们可以拿一个简单的文件来理清思路:
A
B
C
D
如下图:
除了第一行和最后一行处理不一样以外(第一行只执行h,而最后一行只执行G),其余行都是用G、d和h(使用d 的原因是不把中间结果输出)。在sed中有个操作是!,也就是编程语言中的“非”,即不执行,因此,我们可以写出sed命令来倒置一个文件:
JCHJCL01:/tmp/gnuhpc#sed '1!G;h;$!d' phonelist Llewellyn, Lee 817-8823 Jansen, Jan 903-3333 Llewellyn, Lynn 316-6221 Martin, Marty 814-5587 Christopher, Chris 305-5981 Robinson, Robin 411-3745 Patterson, Pat 614-6122 Franklin, Francis 704-3876 Terrell, Terry 617-7989
#p#
5.进阶语法篇之元字符的使用
sed有几个很NB的元字符,这部分往往与正则表达式一起使用能够得到事半功倍的效果。
基本语法:
& : 代表SELECTION中匹配的部分,常用于某个子字符串前后添加字符的操作
\num : num代表匹配子字符串的序号,从1开始,\num表示匹配的子字符串(正则表达式中称为分组),其中子字符串的匹配模式是由圆括号及其转义字符构成。
Task 15:将每个电话号码前加上Tel:
JCHJCL01:/tmp/gnuhpc#sed '/[0-9]\{3\}-[0-9]\{4\}/s//Tel: &/g' phonelist Terrell, Terry Tel: 617-7989 Franklin, Francis Tel: 704-3876 Patterson, Pat Tel: 614-6122 Robinson, Robin Tel: 411-3745 Christopher, Chris Tel: 305-5981 Martin, Marty Tel: 814-5587 Llewellyn, Lynn Tel: 316-6221 Jansen, Jan Tel: 903-3333 Llewellyn, Lee Tel: 817-8823
可以看到前边SELECTION是一个正则表达式来匹配电话号码,也就是0-9三位数-0-9四位数这样一个匹配逻辑,关键是元字符的使用,&代表了匹配的这串电话号码,在前边加上Tel:就是件很随意的事情了。
Task16:电话号码升级,从原来的四位数统一升级为五位数,6开头。
JCHJCL01:/tmp/gnuhpc#sed 's/\([0-9]\{3\}\)-\([0-9]\{4\}\)/\1-6\2/g' phonelist Terrell, Terry 617-67989 Franklin, Francis 704-63876 Patterson, Pat 614-66122 Robinson, Robin 411-63745 Christopher, Chris 305-65981 Martin, Marty 814-65587 Llewellyn, Lynn 316-66221 Jansen, Jan 903-63333 Llewellyn, Lee 817-68823
Task17:将paths文件中的路径用逗号连起来。首先,将每一行都放入hold space(以\n连接起来)而这个中间过程不显示(也就是$!d所表示的除非处理到最后一行,否则都把pattern space删掉),随后在到最后一行时将hold space中的内容放回pattern space(此处用了x,其实g也是可以的),并且把开头的\n去掉后将剩余的\n替换为,最后打印。
JCHJCL01:/tmp/gnuhpc#sed 'H;$!d;${ > x > s/^\n// > s/\n/,/g > }' paths /opt/virtprovider/lib,/var/adm/syslog,/usr/bin/,/usr/local/bin
6.进阶语法篇之改变处理流程操作
有时我们希望对匹配的下一行或多行进行操作,有时我们又希望在处理完毕后马上退出(因为sed会对读入文本的每一行进行操作,即使肉眼看起来明显不匹配也是需要sed先把字符串load进来与SELECTION对照判断的),此时就需要改变处理流程。
基本语法:
n:将之前读入的行(也就是在pattern space中的行)输出到屏幕,然后为将下一行的内容提前读入pattern space(替换上边已经打印的行),后续的命令会应用到新读入的行上。
q: 使用时前边加行号n,表示取前n行,这个在读取大文件的前几行时有很大的作用。
N:将下一行的内容读取并追加到当前模式空间中(用换行符作为连接),并没有输出当前模式空间中的行。注意pattern space含有多行时,正则表达式符号^和$含义分别为^匹配模式空间的最开始,而$是匹配模式空间的最后位置。
D:该命令删除模式空间中从第一个字符到第一个换行符的内容,并且跳转到命令开头重新执行。注意,当模式空间仍有内容时,不读入新的输入行,类似形成一个循环。
P:仅打印模式空间中从第一个字符到第一个换行符的内容,重新在模式空间的内容上执行编辑命令,类似形成一个循环。
:label和b label : 标注一个标签并跳转。例如,下面的例子中模拟了一个if操作存在符合pattern则跳过command2直接执行command3
command1 /pattern/b goto command2 :goto command3
而下边的例子则模拟了一个if else操作,符合pattern时执行command3,不符合时执行command3
command1 /pattern/b dosomething command2 b :dosomething command3
Task18:将QMPath后边的空行删掉
JCHJCL01:/tmp/gnuhpc#sed '/QMPath/{n > /^$/d > }' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
Task19: 取一个大文件的前两行,可以看到同样都是取前两行,由于file这个文件较大,最终导致效率的差别是几十倍。
JCHJCL01:/tmp/gnuhpc#ls -l file -rw-r--r-- 1 root system 78888888 Jan 25 23:20 file JCHJCL01:/tmp/gnuhpc#time sed -n '1,2p' file 1 2 real 0m0.78s user 0m0.35s sys 0m0.13s JCHJCL01:/tmp/gnuhpc#time sed '2q' file 1 2 real 0m0.01s user 0m0.00s sys 0m0.00s
Task20:将多个连续空行缩减为一个空行,非连续空行保留。$q表示最后一行不进行处理,因为由于前边的处理,到了最后一行已经不会出现连续空行了。其余的处理逻辑为:匹配空行,读入下一行,发现下一行还是空行后删除第一个空行然后继续读、处理直到下一行不是空行为止。
JCHJCL01:/tmp/gnuhpc# sed '/^$/{$q N /^\n$/D }' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1 JCHJCL01:/tmp/gnuhpc#
#p#
6.sed技巧拾零篇
本来想介绍一下正则表达,无奈太博大精深,几个例子也说明不了太多,因此正则表达部分可以参考sed手册,里面有比较详尽的讲解。此处举一些常用和手册上没有提及的用例。
[:alnum:]:表示所有的字母和数字
[:digit:]: 表示所有数字
[:upper:]: 表示所有的大写字母
[:lower:] :表示所有的小写字母
Task21:去掉config.ini中的数字,使得config.ini变为一个配置文件模板
JCHJCL01:/tmp/gnuhpc#sed 's/[[:digit:]]//g' config.ini [WQMInfo] ProxyMode= Proxy= RunMode= LastStatisticTime=-- RecordMode= RecordKeyBoard= RecordMouseClick= RecordMouseMove= QMPath= [BROWSE_MODE] ShowToolBar= ShowStatusBar= AutoCloseDialog= MinimizeToHide= [DEVELOP_MODE] ShowToolBar= ShowStatusBar= AutoCloseDialog= MinimizeToHide= [RUN_MODE] ShowToolBar= ShowStatusBar= AutoCloseDialog= MinimizeToHide=
利用SELECTION进行取符合匹配条件的连续多行:
Task22:取得config.ini中WQMInfo段
JCHJCL01:/tmp/gnuhpc#sed -n '/WQMInfo/,/^$/p' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 RecordMouseMove=0 QMPath=
取匹配条件的上N行和下N行:
Task23:取得通讯录中Martin上边的一个人的记录,读入下一行到模式空间,并且判断是否包含Lynn,如果匹配,则打印模式空间中的第一行,如果不匹配,则删除模式空间的第一行,循环处理。
JCHJCL01:/tmp/gnuhpc#sed -n '$!N;/Lynn/!D;/Lynn/P' phonelist Martin, Marty 814-5587
Task24:取得Martin上边包含Martin的所有人记录:
JCHJCL01:/tmp/gnuhpc#sed -n '1,/Martin/p' phonelist Terrell, Terry 617-7989 Franklin, Francis 704-3876 Patterson, Pat 614-6122 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587
Task25:取得Martin上边包含Martin的三条记录:
JCHJCL01:/tmp/gnuhpc#sed -n '1,/Martin/p' phonelist | tail -3 Robinson, Robin 411-3745 Christopher, Chris 305-5981 Martin, Marty 814-5587
Task26:取得通讯录中Martin下边的一个人的记录:
JCHJCL01:/tmp/gnuhpc#sed -n '/Martin/{n > p > }' phonelist Llewellyn, Lynn 316-6221
Task27:取得Martin下边包含Martin的所有人记录:
JCHJCL01:/tmp/gnuhpc#sed -n '/Martin/,$p' phonelist Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333 Llewellyn, Lee 817-8823
Task28:取得Martin下边包含Martin的三条记录:
JCHJCL01:/tmp/gnuhpc#sed -n '/Martin/,$p' phonelist | head -3 Martin, Marty 814-5587 Llewellyn, Lynn 316-6221 Jansen, Jan 903-3333
注释掉某些行:
Task29:假设ini文件行注释为前后两个感叹号,请注释掉RecordMouseMove
JCHJCL01:/tmp/gnuhpc#sed 's/^RecordMouseMove.*/!!&!!/' config.ini [WQMInfo] ProxyMode=0 Proxy= RunMode=1 LastStatisticTime=2012-06-17 RecordMode=0 RecordKeyBoard=1 RecordMouseClick=1 !!RecordMouseMove=0!! QMPath= [BROWSE_MODE] ShowToolBar=0 ShowStatusBar=0 AutoCloseDialog=0 MinimizeToHide=0 [DEVELOP_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=0 MinimizeToHide=1 [RUN_MODE] ShowToolBar=1 ShowStatusBar=1 AutoCloseDialog=1 MinimizeToHide=1
Task30:假设paths文件用#进行注释,则注释掉含有usr的行
JCHJCL01:/tmp/gnuhpc#sed 's/.*usr.*/#&/' paths /opt/virtprovider/lib /var/adm/syslog #/usr/bin/ #/usr/local/bin
一个更简单的方法是:
JCHJCL01:/tmp/gnuhpc#sed '/usr/s/^/#/' paths /opt/virtprovider/lib /var/adm/syslog #/usr/bin/ #/usr/local/bin
Task31:在paths中每行加一个行号和冒号
JCHJCL01:/tmp/gnuhpc#sed = paths | sed 'N;s/\n/:/' 1:/opt/virtprovider/lib 2:/var/adm/syslog 3:/usr/bin/ 4:/usr/local/bin
Task32: 处理XML
JCHJCL01:/tmp/gnuhpc#echo "<Amount>10kg</Amount>" | sed 's#\(<Amount>\)[0-9,a-z]*\(</Amount>\)#\1'100kg'\2#g'
<Amount>100kg</Amount>
Task33:替代一行中多个匹配模式中的其中一个。对于下边的解释:前者替换倒数第二个匹配;后者替换最后一个匹配 。
sed 's/(.*)foo(.*foo)/1bar2/ test.txt sed 's/(.*)foo/1bar/' test.txt
Task34:删除paths的最后2行,在读入第一行后,使用N读入一行,并且如果新读入的下一行不是最后一行,则打印模式空间中的第一行,并且删除,然后接着执行N;如果新读入的一行是文件的最后一行,则删除模式空间中的所有内容(此时pattern space中即为倒数两行)。
JCHJCL01:/tmp/gnuhpc#sed 'N;$!P;$!D;$d' paths /opt/virtprovider/lib /var/adm/syslog
小结篇
本文已几个文本文件为例子,说明了sed的基本用法指南,由于我本人倾向于脚本要具有高度的可移植性,另外同一个任务不一定都要交给一个工具完成,多个工具配合使用,在不太考虑性能的前提下,simpler better,因此诸多高级用法和GNU sed的用法均在此文没有涉及。