Sed 是一个流式处理器,处理文本内容的每一行,按照顺序逐个执行指定的处理命令。更多的 sed 命令工作原理,可以参考 How-sed-Works。网络上到处可见的是 sed 命令的使用示例,所以,这里并不会提供大量的示例命令给大家借鉴,而是强调 sed 命令格式,并介绍一些命令的特殊字符。当我们熟悉了 sed 命令格式和一些特殊字符之后,就可以通过 man sed 的手册查阅 sed 命令参数,无需再花时间翻阅网上的资料。

命令格式

sed options [conditions] command [arguments]
说明: 中括号 […] 表示可选,所以处理命令是必填项。
conditions 分别有行号,行内容的过滤条件,这是可选条件,不加任何过滤条件表示匹配所有行;
command 一般是一个字母,如 d 表示删除,s 表示替换;
arguments 是 command 的参数,如 s/xx/XX/g。

假设我们有一个文件,暂且命名为 daily.txt 每日一句。

  1. 1 The only failure there is is the failure to try.
  2. 2 Everything in life happens according to our time, our clock.
  3. 3 The center of bringing any dream into fruition is self-discipline.
  4. 4 Don't settle for average. Bring the best to your moment.

Sed | Stream editor - 图1

选项 options

选项 说明 用法
-e 追加执行命令 # 删除第一行和第三行
sed -e ‘1d’ -e ‘3d’ file.txt
-f 执行 sed 命令文件 # 执行 script.sed 的命令集合
sed -f script.sed file.txt
-n 静默模式, 表示只输出命中的内容,通常与命令 p 一起使用 # 打印第三和第四行的内容
sed -n ‘3,4p’
-i 表示编辑原文件,也就是说对原文件进行修改 # 替换 $file_name 里的 Good 为 Execellent
sed -i ‘s/Good/Execellent/g’ file.txt
-i “文件缀名” 表示编辑原文件,但修改前使用指定的后缀名备份原文件 # 替换 file.txt 里的 Good 为 Execellent,并将原文件重命名为 file.txt.bak
sed -i “.bak” ‘s/Good/Execellent/g’ file.txt
-E 拓展的正则表达式
Basic Regex vs Extended Regex
# 替换 gray 及 grey 为 blue
sed -E ‘s/gr[ae]y/blue/g’ file.txt
-r 等价于 -E,这是过时的选项,为了保持兼容 # 替换 gray 及 grey 为 blue
sed -r ‘s/gr[ae]y/blue/g’ file.txt

过滤条件(可选)

行号过滤

行号范围 说明 用法
n 指定行号 # 打印第三行
sed -n ‘3 p’ file.txt
n,m 指定行号范围, 如果 n < m, 那么仅选择第 n 行 # 打印第三到第五行
sed -n ‘3,5 p’ file.txt
n,$ 指定第 n 行开始到最后一行 # 打印第三到最后一行
sed -n ‘3,$ p’ file.txt

行内容正则过滤

2.1. 传统斜杠分隔符
匹配格式可以使用传统的斜杠 /xxx/,如 sed -n '/hello world/ p' file.txt

2.2 定义分隔符
也可以使用反斜杠定义分隔符,如 sed -n '\#hello world# p' file.txt,其中就是用了 # 定义了 # 号作为正则的分隔符,等价于 sed -n '\|hello world| p' file.txt,注意到这里使用了 | 竖线作为分隔符。
在过滤一些文件路径时,定义正则分隔符能够简化正则内容的作用,譬如匹配 /usr/bin/mysql,传统使用斜杠的写法是 sed -n '/\/usr\/bin\/mysql/ p' file.txt,如果使用定制分隔符方式,sed -n '\#/usr/bin/mysql#' file.txt,就能在不转义斜杠 / 的前提下完成工作。

2.3. 更多内容
参考 BRE(Basic Regular Expressions) and ERE(Extended Regular Expressions) 获取更多说明和例子,如匹配内容时忽略大小写 sed -n '/HELLO WORLD/I p' file.txt,就能打印出 hello world、Hello World 等所有行数据。

条件取反

在过滤条件的最后添加 ! (感叹号)表示条件取反。例如sed '1,2! d' file.txt 表示保留第一行及第二行,删除其他行。sed -n '/hello world/! p' file.xt 表示打印不包含 hello world 的所有行数据。

匹配多行

可以使用匹配行范围的条件,譬如现在需要匹配 html 内的 div 元素,html 内容如下所示。

<html>
  <div>
    The only failure there is is the failure to try.
  </div>
  <div>
    Don't settle for average. Bring the best to your moment.
  </div>
</html>
sed -n '/<div>/,/<\/div>/ p' input.html

# 输出结果
#  <div>
#    The only failure there is is the failure to try.
#  </div>
#  <div>
#    Don't settle for average. Bring the best to your moment.
#  </div>

<div开始匹配,直到 </div> 结束,如果没能遇到 </div> 则直到文本的结尾。除了使用正则匹配,还能混合使用行号及正则条件进行范围匹配,更多的例子可以参考 。

命令 command [arguments]

s/regex/replacement/[flags]

替换文本内容,格式如 s/regex/replacement/flags,或自定义分隔符,s 的下一个字符就是分隔符,s{分隔符},例如 s|regex|replacement|flags。

regex
正则匹配参考 BRE(Basic Regular Expressions) and ERE(Extended Regular Expressions) 上的说明,同样通过 sed -E 的选项来决定使用的是 BRE 还是 ERE,不过建议使用 ERE。

replacement
将命中正则条件的内容替换成特定的内容,特别说明的是 &\N 替换字符,& 表示匹配的内容,\N 表示匹配的组序号。如 echo 'abcXXXXXXXXXX123' | sed -E 's/(abc).*(123)/\1\2/g' 输出 abc123,其中 \1 表示 abc,\2 表示 123;又例如echo 'def123' | sed 's/def/abc&/g' 输出 abcdef123,其中 & 表示 def。

flags(可选项)
若没有定义 flags 标识,则只会替换一次,如 echo 'aaaa' | sed 's/a/b/' 输出 baaa,想要替换所有字符可以增加 g 标记,如 echo 'aaaa' | sed 's/a/b/g' 就会如期输出 bbbb。

g 全局替换,替换所有命中匹配条件的字符。
Number 替换的连续出现的命中正则的个数,例如 sed 's/a/&:/10' file.txt 等价于 sed 's/aaaaaaaaaa/&:/' file.txt。注意这里的 & 表示命中匹配的字符。
w filename 将替换后的结果保存到指定文件,默认情况下输出到控制台,更多例子参考 https://www.grymoire.com/Unix/Sed.html#uh-10
I
i
正则匹配时忽略大小写,例如 sed 's/abc/123/i' file.txt,其中 ABC、abc、Abc 都会被替换成 123。

q[exit-code]

匹配后退出,并设置程序返回码,例如 sed '/bad/ q 42' file.txt 表示遇到 bad 关键字则返回 42 错误码。

d 删除行

删除一行数据。

a 追加行

在当前匹配行下面插入一行自定义的文本内容。
a text
a \
text

sed 'a hello' file.txt

sed 'a \
hello' file.txt

i 插入行

在当前匹配行上面插入一行自定义的文本内容。
i text
i \
text

sed 'i hello' file.txt

sed 'i \
hello' file.txt

c 改变一行

使用自定义的文本内容替换一行数据。
分别可以使用以下格式
c text
c \
text

sed 'c hello' file.txt

sed 'c \
hello' file.txt

= 行号

在行内容之前新增一行并打印行号,sed '=' file.txt

多命令(分号)

command1;command2;command3;…
多条命令组合使用,可以使用分号(;)连接,对每一行数据按照顺序执行所有的命令,如替换一行里面的所有黄色及红色为蓝色,sed 's/yellow/blue/g; s/red/blue/g' file.txt,还能分别针对多行数据,如删除第一行、第六行、第九行 sed '1d;6d;9d' file.txt

组合命令(大括号)

{
command1
command2

}
类似于分号,但使用 {} 表示多条命令合并为一条指令,并按顺序执行其内部的命令,例如下列例子所示。

# 替换一行里面的所有黄色及红色为蓝色
sed '{
      s/yellow/blue/g
      s/red/blue/g
     }' file.txt

# 删除第一行、第六行、第九行
sed '{
      1d
      6d
      9d
     }' file.txt

参考资料