sed 流编辑器
1. sed 简介:为什么需要 sed?
在普通文本编辑器(如 vim)中删除,替换文本可通过键盘交互式输入来执行。但是,如果在自动化场景或者不需要通过文本编辑器的场景下删除,替换文本该怎么做呢?
sed 编辑器很好的提供了这个功能,sed 编辑器又叫做流编辑器,它基于预先定义的规则来处理数据流,从而实现自动处理文本文件。
2. sed 命令
sed 包括诸如替换,删除,插入,附加等多种处理数据流的命令。
2.1 sed 替换
sed 替换的语法如下所示:
[root@lianhua bash]# cat sed_demo.sh Are you lianhua? yes! I am lianhua. hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed 's/lianhua/me/' sed_demo.sh Are you me? yes! I am me. hi, me nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# cat sed_demo.sh Are you lianhua? yes! I am lianhua. hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
通过上面示例,有三点值得注意:
-
sed 的 s 标记表示这是一条替换规则,替换规则是使用 me 替换文本中的 lianhua。
-
sed 逐行编辑文本 sed_demo.sh 中的流数据,实际的文本并无变化。事实上,sed 会从 STDIN 输入流中读取数据,然后将编辑之后的数据输出到 STDOUT 中。
-
如果数据流中有多个匹配项,sed "s/*/*/" 这种形式只会编辑第一个匹配的数据流。
如果需要全局匹配,则需要在 sed 中加入替换标记,有 4 种替换标记:
-
数字:表明新文本将替换第几处匹配的地方。
-
g: 表明新文本将替换所有匹配的文本。
-
p: 表明原先行的内容要打印出来。
-
w file:将替换的结果写到文件中。
替换标记 g:
[root@lianhua bash]# sed 's/lianhua/me/g' sed_demo.sh Are you me? yes! I am me. hi, me nice to meet u. I am me sheng. Nice to meet you too.
可以看到,第三行的两处 lianhua 都被匹配成 me 了。
替换标记 p:
[root@lianhua bash]# cat sed_demo.sh ... Are you lianhua? yes! I am lianhua. hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed 's/lianhua/me/p' sed_demo.sh ... Are you me? Are you me? yes! I am me. yes! I am me. hi, me nice to meet u. I am lianhua sheng. hi, me nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed -n 's/lianhua/me/p' sed_demo.sh Are you me? yes! I am me. hi, me nice to meet u. I am lianhua sheng.
sed 的 p 标记会打印编辑过的行,通常将它和 sed 的 -n 选项结合使用。 -n 选项禁止 sed 编辑器输出,而 p 会打印编辑过的行,最终的效果是只输出被替换命令修改过的行。
数字替换标记:
[root@lianhua bash]# sed 's/lianhua/me/2' sed_demo.sh ... Are you lianhua? yes! I am lianhua. hi, lianhua nice to meet u. I am me sheng. Nice to meet you too.
匹配文本中第二次出现的匹配项。
w file 替换标记:
[root@lianhua bash]# sed 's/lianhua/me/w test.txt' sed_demo.sh ... Are you me? yes! I am me. hi, me nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# cat test.txt Are you me? yes! I am me. hi, me nice to meet u. I am lianhua sheng.
2.1.1 sed 替换:单行单命令和单行多命令
前面介绍的替换方式都是单行单命令,即逐行根据单条命令规则编辑数据流。在 sed 编辑器中还可以使用单行多命令,即基于多个规则逐行编辑数据流。
单行多命令主要有两种形式:
1. 分号,将多条语句用分号隔开,sed 会逐行依序执行语句:
[root@lianhua bash]# sed 's/lianhua/me/g; s/sheng/huaige/' sed_demo.sh ... Are you me? yes! I am me. hi, me nice to meet u. I am me huaige. Nice to meet you too.
2. 花括号,配合行寻址使用:
[root@lianhua bash]# sed '4{s/lianhua/me/g; s/sheng/huaige/}' sed_demo.sh ... Are you lianhua? yes! I am lianhua. hi, me nice to meet u. I am me huaige. Nice to meet you too.
行寻址 4 标记指定了匹配第四行,将花括号中的语句作用于该行,各语句间用分号隔开。
2.1.2 sed 替换:什么是行寻址?
上小节提到了行寻址这个概念,那么什么是行寻址呢?
sed 编辑器默认对数据流进行逐行编辑,如果需要指定编辑哪一行,则需要使用行寻址来声明 sed 编辑器该编辑哪一行。
sed 有两种形式的行寻址:
-
数字形式的行寻址。
-
文本模式过滤器匹配的行寻址。
2.1.2.1 数字形式的行寻址
数字形式的行寻址通过在匹配语句前加数字或行区间来声明 sed 编辑器的行作用域。
数字形式的行寻址:
[root@lianhua bash]# sed '4s/lianhua/me/g' sed_demo.sh ... Are you lianhua? yes! I am lianhua. hi, me nice to meet u. I am me sheng. Nice to meet you too.
行区间形式的行寻址:
[root@lianhua bash]# sed '1,3s/lianhua/me/g' sed_demo.sh ... Are you me? yes! I am me. hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed '1,$s/lianhua/me/g' sed_demo.sh ... Are you me? yes! I am me. hi, me nice to meet u. I am me sheng. Nice to meet you too.
其中,行区间使用逗号分隔,逗号前表行首,逗号后表行尾。 $ 标记表示数据流中的最后一行。
可将行区间结合花括号一起使用,构建单行多命令匹配:
[root@lianhua bash]# sed '1,${s/lianhua/me/g; s/sheng/huaige/}' sed_demo.sh ... Are you me? yes! I am me. hi, me nice to meet u. I am me huaige. Nice to meet you too.
2.1.2.2 文本模式过滤器匹配的行寻址
文本模式过滤器匹配的行寻址,通过指定文本模式过滤出命令要作用的行,它的格式为:/pattern/command。
示例如下:
[root@lianhua bash]# sed '/lian/s/lianhua/me/' sed_demo.sh ... Are you me? yes! I am me. hi, me nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed '/lian/{s/lianhua/me/; s/sheng/huaige/}' sed_demo.sh ... Are you me? yes! I am me. hi, me nice to meet u. I am lianhua huaige. Nice to meet you too.
其中,/lianhua pattern 表示过滤器将过滤出包含文本 lianhua 的行,命令 command 只作用于过滤出的行。同样的,可以使用花括号构建单行多命令的场景。
2.1.3 sed 替换:这就够了吗? - 多行单命令和多行多命令
上节已经介绍了单行单命令和单行多命令的匹配,但是如果匹配的行不在同一行,如下所示:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
要匹配分布在两行的 lianhua shuaige,该怎么匹配呢?这时候就需要用到多行匹配了。
多行匹配会跨行合并文本然后处理合并的文本数据流,sed 包含了三个处理多行文本的特殊命令:
-
N:将数据流中的下一行加入当前行从而创建一个多行组。
-
D:删除多行组中的一行。
-
P:打印多行组中的一行。
在介绍多行匹配之前,先耐着性子看看 sed 中的 next 命令,通过了解它来打好多行匹配的基础。
2.1.3.1 next 命令
sed 编辑器中可指定 n 标记将编辑操作移动到匹配行的下一行,从而对下一行进行操作。如下所示:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. [root@lianhua bash]# sed '/lianhua/{n; s/shuaige/me/}' sed_demo_d.sh hi, man hi Are you lianhua me? yes! I am lianhua me hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
可以看到,在 sed 命令规则中使用文本模式匹配过滤器匹配 lianhua 这一行,然后花括号多命令行首先使用 next 命令 n 将编辑行移动到匹配行 lianhua 的下一行,然后使用替换命令将改行的 shuaige 替换成 me。并且,可以看到倒数第二行中的 lianhua 并没有被替换,因为 next 没有匹配到它。
2.1.3.2 重头戏:多行合并
从上面 next 命令可知,next 会编辑数据流的下一行。而多行匹配会将下一行数据流合并到当前行,从而操作的是当前行以及下一行的数据流。
1. 多行匹配 N 命令:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
[root@lianhua bash]# sed 'N; s/lianhua.shuaige/me/' sed_demo_d.sh hi, man hi Are you me? yes! I am me hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too.
使用 N 选项将合并下一行到当前行然后处理,第一行 “hi, man” 和 第二行 “hi” 合并,匹配项中不匹配。第三行和第四行合并,符合匹配项 “lianhua.shuaige”。其中 . 是通配符,然后将其替换依次类推...
当使用多行匹配 N 命令时,要特别注意行尾的文本数据流是否可合并,如果行尾的数据流不能作为上一数据流的上一行合并那么该行将不会被合并:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed 'N; s/lianhua.shuaige/me/' sed_demo_d.sh hi, man hi Are you me? yes! I am me hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige
如上所示,行尾的 “lianhua shuaige”并没有被合并。解决方法也不难,可以先对行尾的文本匹配,然后在使用多行匹配匹配其它行:
[root@lianhua bash]# sed '$s/lianhua.shuaige/me/; N; s/lianhua.shuaige/me/' sed_demo_d.sh hi, man hi Are you me? yes! I am me hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. me
2. 多行匹配 D 命令:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed 'N; /lianhua.shuaige/d' sed_demo_d.sh hi, man hi hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed 'N; /lianhua.shuaige/D' sed_demo_d.sh hi, man hi shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. lianhua shuaige
将单行删除命令 d 和多行匹配特殊命令 N 结合使用时,d 会将合并的数据流中所有匹配项都删除。而,使用多行删除命令 D 时,仅删除合并数据流的第一行匹配项,且删除到换行符截至。上例中,第三行 lianhua 所在项被删除,而第四行匹配项 shuaige 依然存在。
3. 多行打印 P 命令:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed -n 'N; /lianhua.shuaige/p' sed_demo_d.sh Are you lianhua shuaige? yes! I am lianhua shuaige [root@lianhua bash]# sed -n 'N; /lianhua.shuaige/P' sed_demo_d.sh Are you lianhua yes! I am lianhua
将单行打印命令 p 和多行匹配特殊命令 N 结合使用时,p 会将合并数据流的所有匹配项打印出来。而,使用多行打印命令 P 时,仅打印合并数据流的第一行匹配项,且打印到换行符截至。
2.1.3.3 多行单命令和多行多命令
上面两节介绍的都是多行单命令,那么类似的,多行合并也有多命令的形式。它们是:分号分隔多条语句和文本模式匹配加花括号的形式:
1. 分号分隔多条语句:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed 'N; s/lianhua.shuaige/me/; s/hi/hello/' sed_demo_d.sh hello, man hi # 想一想,这里的 hi 为什么没有替换呢? Are you me? yes! I am me hello, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige
2. 文本模式匹配加花括号:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed '/lianhua/{N; s/lianhua.shuaige/me/}' sed_demo_d.sh hi, man hi Are you me? yes! I am me hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige
2.1.4 sed 替换:特殊字符的替换
替换是 sed 编辑器很常用的命令,除了普通字符的替换通常也会包含特殊字符的替换,如正斜线,空格,换行符,单双引号等的替换。
1. 正斜线替换:
[root@lianhua bash]# cat sed_demo_s.sh hi, man hi what's the directory right now? the directory is /home/lianhua/ what's you address? well, my address is https://10.10.10.10 no,man, i mean the physical address ok, my physical address is "10.10.10.10" no,man, i mean your home address haha, i got your point, my home address is '00 01 6C 06 A6 00' you haven't got my point... [root@lianhua bash]# sed -n '/directory/{s/\/home\/lianhua\//home/p}; s/https:\/\//ftp:/p' sed_demo_s.sh the directory is home well, my address is ftp:10.10.10.10
当替换正斜线时,需使用反斜杠进行转义。有时候这样的写法会带来一些困惑,sed 允许使用其它字符来代替字符串分隔符,从而使得替换更简洁:
[root@lianhua bash]# sed -n '/directory/{s!/home/lianhua/!home!p}; s/https:\/\//ftp:/p' sed_demo_s.sh the directory is home well, my address is ftp:10.10.10.10 [root@lianhua bash]# sed -n '/directory/{s#/home/lianhua/#home#p}; s/https:\/\//ftp:/p' sed_demo_s.sh the directory is home well, my address is ftp:10.10.10.10
2. 空格替换:
[root@lianhua bash]# sed -n '/my home address/{n ; s/ /:/gp}' sed_demo_s.sh # 替换分隔符中间保留空格即为匹配项 '00:01:6C:06:A6:00'
3. 单引号替换:
[root@lianhua bash]# sed -n '/my home address/{n ; s/ /:/g; s/'//pg}' sed_demo_s.sh > ' sed: -e expression #1, char 40: extra characters after command [root@lianhua bash]# sed -n '/physical address/{n ; s/\'//gp}' sed_demo_s.sh > ' sed: -e expression #1, char 46: unterminated `s' command [root@lianhua bash]# sed -n "/my home address/{n ; s/ /:/g; s/'//pg}" sed_demo_s.sh 00:01:6C:06:A6:00
单引号替换需将 sed 编辑规则置于双引号中,而不是放在单引号里,放在单引号内内层的单引号将无法被识别。并且,单引号也不支持转义表示。
4. 双引号替换:
[root@lianhua bash]# sed -n '/physical address/{n ; s/"//gp}' sed_demo_s.sh ok, my physical address is 10.10.10.10 [root@lianhua bash]# sed -n "/physical address/{n ; s/\"//gp}" sed_demo_s.sh ok, my physical address is 10.10.10.10
双引号替换可将 sed 编辑规则置于单引号内,或者使用反斜杠对双引号转义也是可以的。
2.2 sed 删除
前面花了很多篇幅介绍 sed 中的替换命令,当然 sed 编辑器也支持其它命令。sed 的 d 命令表示删除,它会根据预定义规则逐行删除匹配项:
[root@lianhua bash]# cat sed_demo_d.sh hi, man hi Are you lianhua shuaige? yes! I am lianhua shuaige hi, lianhua nice to meet u. I am lianhua sheng. Nice to meet you too. lianhua shuaige [root@lianhua bash]# sed '/lianhua/d' sed_demo_d.sh hi, man hi shuaige? shuaige Nice to meet you too.
sed 匹配到含有字符 lianhua 的行,然后删除这些行。
2.3 sed 插入和附加
sed 中的 i 命令表示插入,a 表示附加。它们的区别时插入会在匹配行的上一行插入文本,而附加则是在匹配行的下一行附加文本。
[root@lianhua bash]# cat sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too. [root@lianhua bash]# cat sed_demo_dd.sh | sed '3i\are you' | sed "5a\no i'm not.." hi, man hi are you shuaige? shuaige no i'm not.. Nice to meet you too.
2.4 sed 修改
sed 中的 c 命令表示修改,它会修改数据流中整行的内容。
[root@lianhua bash]# cat sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too. [root@lianhua bash]# cat sed_demo_dd.sh | sed '3c\lianhua?' | sed '/shuaige/c\lianhua' # 同时用到了数字寻址和文本匹配寻址 hi, man hi lianhua? lianhua Nice to meet you too.
2.5 sed 特殊命令
sed 除了替换,删除等命令外,还包括一些特殊命令,如排除命令 !和行号命令 = 等:
1. 排除命令
在命令前加排除命令 ! 表示预定义规则作用于除匹配行之外的其它行:
[root@lianhua ~]$ openstack port list | sed '/demo/!d' | 40d7-9c56-0a419c766316 | demo | fa:16:3e:f7:91:89 | ip_address='192.168.2.131', subnet_id='afd835f7f6ee' | ACTIVE |
2. 行号命令
sed 中使用 = 可显示每行的行号,通常将它和替换命令一起使用:
[root@lianhua bash]# cat sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too. [root@lianhua bash]# cat sed_demo_dd.sh | sed '=' 1 hi, man 2 hi 3 shuaige? 4 shuaige 5 Nice to meet you too. [root@lianhua bash]# cat sed_demo_dd.sh | sed '=' | sed 'N; s/\n/ /' 1 hi, man 2 hi 3 shuaige? 4 shuaige 5 Nice to meet you too.
2.6 sed 特殊字符
sed 中也包括一些特殊字符,如表示空白行的字符 ^$:
[root@lianhua bash]# cat sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too. [root@lianhua bash]# sed 's/\n/a/' sed_demo_dd.sh # 空白字符,不是换行符,使用换行符并不能匹配到它 hi, man hi shuaige? shuaige Nice to meet you too. [root@lianhua bash]# sed 's/^$/a/' sed_demo_dd.sh hi, man hi shuaige? a shuaige a Nice to meet you too. [root@lianhua bash]# sed '/^$/d' sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too.
3. sed 的两种空间:模式空间和保持空间
sed 编辑器有两种非常重要的空间:模式空间和保持空间。模式空间是 sed 编辑器在执行命令时保存待检查文本的空间。保持空间是 sed 编辑器在处理模式空间中的某些行时,用来临时保存一些行的空间。
有以下几个命令可以用来做为模式空间和保持空间的互相转化:
-
h:将模式空间复制到保持空间。
-
H:将模式空间附加到保持空间。
-
g:将保持空间复制到模式空间。
-
G:将保持空间附加到模式空间。
-
x:交换模式空间和保持空间的内容。
举例如下:
[root@lianhua bash]# cat sed_demo_dd.sh hi, man hi shuaige? shuaige [root@lianhua bash]# sed -n '/shuaige?/{h; p; n; p; g; p}' sed_demo_dd.sh shuaige? shuaige shuaige?
上例中,使用文本匹配过滤出 "shuaige?" 行,在该行通过 h 命令将模式空间的 "shuaige?" 复制到保持空间,p 命令打印模式空间的文本行,接着 n 向下移动一行,p 打印该行即 shuaige,然后 g 命令将保持空间的内容复制到模式空间,在通过 p 命令打印模式空间的当前内容,即 "shuaige?"。
结合排除命令可以实现翻转文本的效果:
[root@lianhua bash]# cat sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too. [root@lianhua bash]# sed -n '{1!G; h; $p}' sed_demo_dd.sh Nice to meet you too. shuaige shuaige? hi hi, man
使用 Liunx 自带的 tac 命令也可以翻转文本:
[root@lianhua bash]# tac sed_demo_dd.sh Nice to meet you too. shuaige shuaige? hi hi, man
组合使用 h G 命令看看会发生什么效果,思考这是为什么:
[root@lianhua bash]# sed -n '{h; G; p}' sed_demo_dd.sh hi, man hi, man hi hi shuaige? shuaige? shuaige shuaige Nice to meet you too. Nice to meet you too. [root@lianhua bash]# sed -n '{h; p; G}' sed_demo_dd.sh hi, man hi shuaige? shuaige Nice to meet you too.
芝兰生于空谷,不以无人而不芳。