IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

系统工程师的自我修养- sed篇

gnuhpc的百草园和三味书屋 2019-08-11 11:31:51 累计浏览 2,435 次
本机暂存

注:本文除特殊注明外均针对传统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中的字符串只是用于临时处理的中间结果,是不会被输出的(在本文第四章会有介绍,此时不了解不影响阅读此文)。

clip_image002

2.用途篇

学习一个脚本语言,了解了基本原理后就要调研一下这个东东是不是满足完成你任务的需求。一提到sed,肯定就会牵扯到awk,它具体的功能会在后边的awk篇进行叙述。对于同一个任务,sed和awk都有可能解决,因此对于sed、awk的用途每个人都有不同的习惯和自己擅长的用法,在实践中,我个人习惯于用sed进行行处理,也就是根据sed的原理,需要进行一行行的处理操作时优先使用sed。而用awk更多的进行列处理。而在具体任务上,由于sed强大的替换能力和编辑能力,我常常用sed作为编辑器,而awk 作为信息获取和格式处理输出的能力。

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#

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

如下图:

sed插图1

除了第一行和最后一行处理不一样以外(第一行只执行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

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#

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的用法均在此文没有涉及。


同分类推荐文章

  1. 从零重建 macOS 开发机:可复现的环境初始化流程 (2026-06-14 20:36:00)
  2. 百度物理网络监控工具开源第二弹:毫秒级监控工具 baize,让你的网络问题无处遁形 (2026-06-11 08:10:28)
  3. How to Set Up Homebrew Tap for Private CLI Tools: A Complete Guide (2026-05-27 02:13:03)

查看更多 DevOps 文章 →

建议继续学习

  1. 28个Unix/Linux的命令行神器 (累计阅读 16,789)
  2. 利用find和sed批量替换文件内容 (累计阅读 11,456)
  3. perl更新/修改/删除文本文件内容 (累计阅读 10,646)
  4. AWK 简明教程 (累计阅读 9,363)
  5. Bash脚本15分钟进阶教程 (累计阅读 9,054)
  6. AWK介绍 (累计阅读 6,707)
  7. awk 实例之二维数组 (累计阅读 6,009)
  8. Perl命令行常见用法及技巧 (累计阅读 5,912)
  9. 正则表达式的与或非 (累计阅读 5,866)
  10. 学习Grep,Sed中的正则 (累计阅读 5,404)