sed案例
替换
替换命令有许多用法,下面是它的详细的用法:
[address]s/pattern/replacement/flags
这里修饰替换的标志flags是:
选项 | 描述 |
n | 1到512之间的一个数字,表示对文本模式中指定模式第n次出现的情况进行替换 |
g | 对模式空间的所有出现的情况进行全局更改,而没有g是通常只有第一次出现的情况被取代 |
p | 打印模式空间的内容 |
w | 将模式空间的内容写到文件file 中 |
替换命令应用于与address 匹配的行。如果没有指定地址,那么就应用于Pattern匹配的所有行。如果正则表达式作为地址来提供,并且没有指定模式,那么替换命令匹配由地址匹配的内容。当替换命令是应用于同一个地址上的多个命令之一时,这可能会非常有用。可以参看本章后面的“检验参考页”一节中的示例。
和地址不同的是,地址需要一个作为定界符的斜杠(/) ,而正则表达式可以用任意字符来分隔,只有换行符除外。因此,如果模式包含斜杠,那么可以选择另一个字符作为定界符,例如井号。
s#/usr/mail#/usr2/mail#
注意,定界符出现了3次而且在replacement之后是必需的。不管使用哪种定界符,如果它出现在正则表达式中,或者在替换文本中,那么就用反斜杠来转义它。
从前,计算机用固定长度的记录来存储文本。一行在出现许多字符(一般为80个)之后结束,然后开始下一行。数据中没有显式的字符来标记一行的结束和下一行的开始,每一行都有相同的(固定的)数量字符。现在的系统比较灵活,它们使用特殊的字符(称为换行符(newline))标记行的结束,这样就允许行行的长度为任意。
因为在内部存储时换行符只是一个字符,所以正则表达式可以使用“\n”来匹配嵌入的换行符。
Replacement是一个字符串,用来替换与正则表达式匹配的内容,在replacement部分,只用下列字符有特殊含义:
选项 | 描述 |
& | 用正则表达式匹配的内容进行替换 |
\n | 匹配第n个字串(n是一个数字),这个字串以前在pattern 中用“、(”和“、)”指定 |
\ | 当在替换部分包含“与“符号(&),反斜杠(\)和替换命令的定界符时可用\转义它们,另外,它用于转义换行符并创建多行replacement字符串。 |
&的用法
//文本test1的内容如下 [root@RedHat ~]# cat test1 Alice Ford, 22 East Broadway, Richmond VA //将11替换成112 [root@RedHat ~]# sed 's/11/&2/' test1 Alice Ford, 112 East Broadway, Richmond VA //将11替换成11 2 [root@RedHat ~]# sed 's/11/& 2/' test1 Alice Ford, 11 2 East Broadway, Richmond VA //将11替换成211 [root@RedHat ~]# sed 's/11/2&/' test1 Alice Ford, 211 East Broadway, Richmond VA //匹配2位的整数在其后面加上2 [root@RedHat ~]# sed -r 's/[0-9]{1}/&2/' test1 Alice Ford, 112 East Broadway, Richmond VA
替换元字符
替换元字符是反斜杠(\)、“与”符号(&)和\n,反斜杠一般用于转义其他的元字符,但是他在替换字符串中也用于包含换行符。
来自于将troff文件转换成Ventura Publisher的 ASCIl输入格式,它将下面的troff行:
.Ah "Major Heading"
转换成类似的Ventura Publisher行:
@A HEAD = Major Heading
这个问题中的难点是这一行需要前后都有空行,这是一个编写多行替换字符串的问题。
//文本test0内容如下 [root@RedHat ~]# cat test0 .Ah "Major Heading" [root@RedHat ~]# sed ' > /^\.Ah */{ > s/\.Ah */\ > \ > @A HEAD = / > s/"//g > s/$/\ > / > }' test0 @A HEAD = Major Heading
第一个替换命令用两个换行符和“@A HEAD =”取代“.Ah”,在行结尾处有必要用反斜杠转义换行符。第二个替换删除了引号。最后一个命令匹配模式空间中的行的结尾(不是嵌入的换行符),并在它后面添加一个换行符。
作为元字符,“与”符号(&)表示模式匹配的范围,不是被匹配的行。可以使用“与”符号匹配一个单词并且用troff 请求来包围它。下面的示例用点数请求包围一个单词:
s/UNIX/\\s-2&\\s0/g
因为反斜杠也是替换字符串中的元字符,所以需要用两个反斜杠来输出一个反斜杠,替换字符串中的“&”表示“UNIX”。
//文本test3内容如下 [root@RedHat ~]# cat test3 on the UNIX Operating System. [root@RedHat ~]# sed 's/UNIX/\\s-2&\\s0/g' test3 on the \s-2UNIX\s0 Operating System.
当正则表达式匹配单词的变化时,“与”符号特别有用。它允许指定一个可变的替换字符串,该字符串相当于匹配的内容与实际内容匹配的字符创。例如,假设要用圆括号括住文档中对已编号部分的任意交叉引用。换句话说,任意诸如“See Section 2.1”或“See Section 21.6”的引用都应该出现在圆括号中,如“(See Section 21.6)”。正则表达式可以匹配数字的不同组合,所以在替换字符串中可以使用“&”并括起所匹配的内容。
//文本test2内容如下 [root@RedHat ~]# cat test2 See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 //给See Section (任意小数)两边加上括号 [root@RedHat ~]# sed 's/See Section [1-9][0-9]*\.[1-9][0-9]*/(&)/' test2 (See Section 2.1) (See Section 31.56) (See Section 21.6) (See Section 152.36) (See Section 11.555) (See Section 1.64)
“与”符号用于在替换字符创中引用整个匹配内容。
现在,我们来看一种元字符,它用于选择被匹配的字符串的任意独立部分,并且在替换字符创中回调它。在sed中转义的圆括号括住正则表达式的任意部分并且保存它以备回调。一行最多允许“保存”9次。“\n”用于回调被保存的匹配部分,n是从1到9的数字,用于饮用特殊“保存的”备用字符串。
例如,当节号出现在交叉引用中时要表示为用粗体,可以编写下面的替换:
//文本test2内容如下 [root@RedHat ~]# cat test2 See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 //将小数加粗 [root@RedHat ~]# sed -r 's/(See Section) ([1-9][0-9]*\.[1-9][0-9]*)/\1 \\fB\2\\fP/' test2 See Section \fB1.4\fP See Section \fB12.34\fP See Section \fB12.9\fP See Section \fB789.11\fP See Section \fB55.666\fP See Section \fB3.26\fP
删除
删除命令(d)采用一个地址,如果行匹配这个地址就删除模式空间的内容。
删除命令还是一个可以改变脚本中的控制流的命令,这是因为一旦执行这个命令,那么在“空的”模式空间中就不会再有命令执行,删除命令会导致读取新输入行,而编辑脚本则从头开始新的一轮。
重要的是:如果某行匹配这个地址,那么就删除整个行,而不只是删除行中匹配的部分(要删除行的一部分,可以使用替换命令并指定一个空的替换)。
//文本test2内容如下 [root@RedHat ~]# cat test2 See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 //删除整数部分为1位的小数的所在行的数据 [root@RedHat ~]# sed -r '/See Section [1-9]\.[1-9][0-9]*/d' test2 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555
追加、插入和更改
追加(a)、插入(i)和更改(c)命令提供了通常在交互式编辑器(例如vi)中所选的编辑功能。你会奇怪地发现、可以使用这些相同的命令在非交互编辑器中“输入”文本。这些命令的语法在sed中不常用,因为它们必须在多行上来指定。语法如下:
追加[line-address]a\ text 插入[line-address]i\ text 更改[address]c\ text
插入命令将所提供的文本放置在模式空间的当前行之前。追加命令将文本放置在当前行之后。更改命令用所提供的文本取代模式空间的内容。
//文本test2内容如下 [root@RedHat ~]# cat test2 See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 //匹配21.6所在行,在其上面加上123\n456 [root@RedHat ~]# sed '/21\.6/i\123\n456' test2 See Section 2.1 See Section 31.56 123 456 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64
追加命令和插入命令只应用于单个行地址,而不是一个范围内的行。然而,更改命令可以处理一个范围内的行。在这种情况下,它用一个文本备份取代所有被寻址的行。换句话说,它删除这个范围中的所有行,但是提供的文本只被输出一次。例如,当下面的脚本在包含邮件消息的文件上运行时:
//文本test内容如下 [root@RedHat ~]# cat test Here are the books that you requested Yes, it is a good book for children It is amzing to think that it was called a "harmful book" when once you get to the end of the book, you can't believe //匹配H开头空行结尾,将其更改为123 [root@RedHat ~]# sed '/^H/,/^$/c123' test 123 It is amzing to think that it was called a "harmful book" when once you get to the end of the book, you can't believe
转换
转换命令是特有的,不仅因为它在所有的sed命令中拥有最小的肋记符,这个命令按位置将字符串abc 中的每个字符,都转换成字符串xyz中的等价字符。它的语法如下:
[address]y/abc/xyz/
替换根据字符的位置来进行。因此,它没有“词”的概念。这样,在该行上的任何地方的“a”都被换成了“x”,而不管它后面是否跟有“b”。这个命令的一个可能的用处是用大写字母替换对应的小写字母,
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
这个命令影响整个模式空间的所有内容。如果想在输入行上转换单个单词,那么通过使用保持空间可以完成。(大致过程是:输出更改单的那一行之前的所有行,删除这些行,将单词后面的行复制到保持空间,转换这个单词,然后将保持空间的内容追加到模式空间)。
//文本test内容如下 [root@RedHat ~]# cat test Here are the books that you requested Yes, it is a good book for children It is amzing to think that it was called a "harmful book" when once you get to the end of the book, you can't believe //将文本中a转换为1,b转换为2,c转换为3,d转换为4,e转换为5,f转换为6 [root@RedHat ~]# sed 'y/abcdef/123456/' test H5r5 1r5 th5 2ooks th1t you r5qu5st54 Y5s, it is 1 goo4 2ook 6or 3hil4r5n It is 1mzing to think th1t it w1s 31ll54 1 "h1rm6ul 2ook" wh5n on35 you g5t to th5 5n4 o6 th5 2ook, you 31n't 25li5v5
打印
打印命令(p)输出模式空间的内容。它既不清除模式空间也不改变脚本中的控制流。然而,它频繁地用在改变流控制的命令(d,N,b)之前。除非抑制(-n)默认的输出,否则打印命令将输出行的重复复制。当抑制默认的输出或者当通过程序的流控制来避免到达脚本的底部时,可能会使用它。
打印行号
跟在地址后面的等号(=)打印被匹配的行的行号。除非抑制行的自动输出,行号和行本身将被打印。它的语法如下:
[line-address]=
这个命令不能对一个范围内的行进行操作。
//文本test2内容如下 [root@RedHat ~]# cat test2 See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 //在每行前打印行号 [root@RedHat ~]# sed -n '/[1-9][0-9]*\.[1-9][0-9]*/{=;p}' test2 1 See Section 2.1 2 See Section 31.56 3 See Section 21.6 4 See Section 152.36 5 See Section 11.555 6 See Section 1.64
下一步
下一步(next)命令(n)输出模式空间的内容,然后读取输入的下一行,而不用返回到脚本的顶端。它的语法如下:
[address]n
next命令改变了正常的流控制,直到到达脚本的底部才会输出模式空间的内容,它总是在读入新行之后从脚本的顶端开始。实际上,next命令导致输入的下一行取代横穿空间中的当前行,脚本中的后续命令应用于替换后的行,而不是当前行。如果没有抑制默认输出,那么在替换发生之前会打印当前行。
下面我们来看next命令的示例,在这个例子中,当空行跟随一个匹配模式的行时,则删除该空行。在这种情况中,假设作者已经在节标题宏(Yes)之后插入一个空行。我们想要删除这个窄而不是删除文件中所有的空行。下面是示例:
//文本test内容如下 [root@RedHat ~]# cat test Here are the books that you requested Yes, it is a good book for children It is amzing to think that it was called a "harmful book" when once you get to the end of the book, you can't believe //删除空行 [root@RedHat ~]# sed '/Yes/{n;d}' test Here are the books that you requested Yes, it is a good book for children It is amzing to think that it was called a "harmful book" when once you get to the end of the book, you can't believe
可以按下面的方式阅读这个脚本:匹配字符串"Yes"的行,然后打印那一行并读入下一行。如果那一行为空,则删除它。大括号用于在同一个地址应用多个命令。
在较长的脚本中,必须记住出现在next命令之前的命令不会应用于新的输入行,而且出现在后面的命令不应用于旧的输入行。
读和写文件
[line-address]r file [address]w file
读命令将由file指定的文件确定的行之后的内容读入模式空间。它不能对一个范围内的行进行操作。写命令将模式空间的内容写到file 中。
在命令和文件名之前必须有一个空格(空格后到换行符前的每个字符都被当做文件名.因此,前导的和嵌入的空格也是文件名的一部分)。如果文件不存在,读命令也不会报错。如果写命令中指定的文件不存在,将创建一个文件;如果文件已经存在,那么写命令将在每次调用脚本时改写它。如果一个脚本中有多个指令写到同一个文件中,那么每个写命令都将内容追加到这个文件中。而且,每个脚本最多只能打开10个文件。
读命令对于将一个文件的内容插入到另一个文件的特定位置是很有用的。例如,假设有一组文件并且每个文件都应以相同的一个或两个段落的语句结束,使用sed脚本可以在必要时分别单独对结束部分进行维护。
//将test2文件的内容写入到test文件内容的匹配Yes行的下面 [root@RedHat ~]# sed '/Yes/r test2' test Here are the books that you requested Yes, it is a good book for children See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 It is amzing to think that it was called a "harmful book" when once you get to the end of the book, you can't believe
退出
退出命令(q)会使sed停止读取新的输入行(并停止将它们发送到输出)。它的语法为:
[line-address]q
它只适用于单行的地址,一旦找到和address 匹配的行,那么脚本就结束。
//文本test2内容如下 [root@RedHat ~]# cat test2 See Section 2.1 See Section 31.56 See Section 21.6 See Section 152.36 See Section 11.555 See Section 1.64 //一直匹配直到找到21.6所在行 [root@RedHat ~]# sed '/21\.6/q' test2 See Section 2.1 See Section 31.56 See Section 21.6 //匹配3行 [root@RedHat ~]# sed '3q' test2 See Section 2.1 See Section 31.56 See Section 21.6
高级sed命令
高级命令分成了3个组:
- 处理了多行模式空间(N、D、P)。 - 采用保持空间来保存模式空间的内容并使它可用于后续的命令(H、h、G、g、x) 。 - 编写使用分支和条件指令的脚本来更改控制流(:、b、 t)。
多行模式空间
在前面正则表达式的讨论中,我们强调模式匹配是面向行的。像grep这样的程序尝试在单个输入行上匹配一个模式。这就使它很难匹配一个在一行的结尾处开始。并在下一行的开始处结束的短语。其他一些模式只有当在多行上重复时才有意义。
sed能查看模式空间的多个行,这就是允许匹配模式扩展到多行上,3个多行命令(N、D、P))对应于小写字母的基本命令(n、d、p)。例如,删除命令(D)是删除命令(d)的多行形式。区别是:d删除模式空间的内容,D只删除多行模式空间的第一行。
N命令
追加下一行
多行Next(N)命令通过读取新的输入行,并将它添加到模式空间的现有内容之后来创建多行模式空间。模式空间最初的内容和新的输入行之间用换行符分隔。在模式空间中嵌入的换行符可以利用转义序列“\n”来匹配。在多行模式空间中,元字符“^”匹配空间中的第一个字条,而不匹配换行符后面的字符。同样,“$”只匹配模式空间中最后的换行符,而不匹配任何嵌入的换行符,在执行next命令之后,控制将被传递给脚本中的后续命令。
Next命令与next命令不同,next输出模式空间的内容,然后读取新的输入行。next命令不创建多行模式空间。
例如,我们假设想要将“Owner and Operator Guide”换成“lnstallation Guide”,但是我们发现它出现在文件中的两行上,“Operator”和“Guide”被分开了。
//文本test内容如下 [root@RedHat ~]# cat text Consult Section 3.1 in the Owner and Operator Guide for a description of the tape drives available on your system. //匹配以Operator结尾以及下行的Guide,合并成一行替换成Installation Guide [root@RedHat ~]# sed '/Operator$/{N;s/Operator\nGuide/Installation Guide/g}' text Consult Section 3.1 in the Owner and Installation Guide for a description of the tape drives available on your system.
D命令
d 删除当前模式空间内容(不在传到标准输出)并放弃之后的命令,读取新内容重新执行sed D 删除当前模式空间开端至\n换行符内容(不在传到标准输出)并放弃之后的命令,但是剩余模式空间内容重新执行sed
[root@RedHat ~]# cat t1 This is followed 1 This is followed 2 This is followed 3 This is followed 4 This is end
有偶数空行的时候,空行会被删除,奇数空行的时候,会有一行被保留下来,并且加上下一个空行复读上面的步骤,直到匹配到的空行被完全输出,如果遇到第三个空行,并且下一行不为空,那么删除命令就不会被执行,因此空行被输出。
效果如下:
[root@RedHat ~]# sed '/^$/{N;/^\n$/d}' t1 This is followed 1 This is followed 2 This is followed 3 This is followed 4 This is end
多行Delete命令完成工作的原因是,当遇到两个空行时,Delete 命令只删除两个空行中的第一个。下一次遍历该脚本时,这个空行将导致另一行被读入模式空间。如果那行不为空,那么两行都输出,因此确保了输出一个空行。换句话说,当模式空间中有两个空行时、只有第一个空行被删除。当一个空行后面.跟有文本时,模式空间可以正常输出。
效果如下:
[root@RedHat ~]# sed '/^$/{N;/^\n$/D}' t1 This is followed 1 This is followed 2 This is followed 3 This is followed 4 This is end
P命令
同 d 和 D 之间的区别一样,P(大写)命令和单行打印命令 p(小写)不同,对于具有多行数据的缓冲区来说,它只会打印缓冲区中的第一行,也就是首个换行符之前的所有内容。
[root@RedHat ~]# cat t2 Here are examples of the UNIX System. Where UNIX System appears, it should be the UNIX Operating System
替换命令匹配“\nSystem” ,并且用“Operat ing\nSystem”取代它。保留换行是很重要的,否则模式空间中就有只一行。注意Print和Delete命令的顺序。
效果如下:
[root@RedHat ~]# sed ' /UNIX$/{ N /\nSystem/{ s// Operating &/ P D }}' t2 Here are examples of the UNIX Operating System. Where UNIX Operating System appears, it should be the UNIX Operating System
如果先删除的话
效果如下:
[root@RedHat ~]# sed ' /UNIX$/{ N /\nSystem/{ s// Operating &/ D P }}' t2 System appears, it should be the UNIX Operating System
模式空间是容纳当前输入行的缓冲区。还有一个称为保持空间(holdspace)的顶留(set-aside) 缓冲区。模式空间的内容可以复制到保持空间,而且保持空间的内容也可以复制到模式空间。有一组命令用于在保持空间和模式空间之间移动数据。保持空间用于临时存储。单独的命令不能寻址保持空间或者更改它的内容。
保持空间最常的用途是,当改变模式空间中的原始内容时,用于保留当前输入行的副本。影响模式空间的命令有:
命令 | 缩写 | 功能 |
Hold | h或H | 将模式空间的内容复制或追加到保持空间 |
Get | g或G | 将保持空间的内容复制或追加到模式空间 |
Exchange | x | 交换保持空间或模式空间的内容 |
[root@RedHat ~]# cat t3 1 2 11 22 111 222
把第一行复制到保持空间,清楚模式空间,然后sed将第二行读入模式空间,并且将保持空间的行追加到模式空间的结尾。
//效果如下
[root@RedHat ~]# sed ' > /1/{ > h > d > } > /2/{ > G > }' t3 2 1 22 11 222 111
[root@RedHat ~]# sed ' /1/{ h d } /2/{ g }' t3 1 11 111
x命令
构建文本块
保持空间可用于在输出行块之前收集它们,一些troff请求和宏是面向块的,这命令中必须包围文件块。通常在块开始处的代码用启用格式,而大块结尾处的代码用禁用格式。HTML编码的文档还包含许多面向块的结构。例如,“《p》”和“《/p》”分别用于开始和结束一个段落。输入文件包含由可变长度的行组成的段落,段落之间都有一个空行。因此,脚本必须将空行之前的所有行收集到保持空间。检索保持空间的内容并且用段落标签包围这些内容。
//效果如下 [root@RedHat ~]# cat t4 hello world hello nihao abc def 123 456 end [root@RedHat ~]# sed ' > /^$/!{ > H > d > } > /^$/{ > x > s/^\n/<p>/ > s/$/<\/p>/ > G > }' t4 <p>hello world hello nihao</p> <p>abc def</p> <p>123 456</p>