Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

3. sed

sed 意为流编辑器(Stream Editor),在 Shell 脚本和 Makefile 中作为过滤器使用非常普遍,也就是把前一个程序的输出引入 sed 的输入,经过一系列编辑命令转换为另一种格式输出。sed vi 都源于早期 UNIX 的ed 工具,所以很多sed 命令和vi 的末行命令是相同的。

sed 命令行的基本格式为

sed option 'script' file1 file2 ...
sed option -f scriptfile file1 file2 ...

sed 处理的文件既可以由标准输入重定向得到,也可以当命令行参数传入,命令行参数可以一次传入多个文件,sed 会依次处理。sed 的编辑命令可以直接当命令行参数传入,也可以写成一个脚本文件然后用-f 参数指定,编辑命令的格式为

/pattern/action

其中 pattern 是正则表达式, action 是编辑操作。 sed 程序一行一行读出待处理文件,如果某一行与 pattern 匹配,则执行相应的 action ,如果一条命令没有 pattern 而只有 action ,这个 action 将作用于待处理文件的每一行。

表 32.5. 常用的 sed 命令

命令含义
/pattern/p打印匹配 pattern 的行
/pattern/d删除匹配 pattern 的行
/pattern/s/pattern1/pattern2/查找符合 pattern 的行,将该行第一个匹配 pattern1 的字符串替换为 pattern2
/pattern/s/pattern1/pattern2/g查找符合 pattern 的行,将该行所有匹配 pattern1 的字符串替换为 pattern2

使用 p 命令需要注意, sed 是把待处理文件的内容连同处理结果一起输出到标准输出的,因此 p 命令表示除了把文件内容打印出来之外还额外打印一遍匹配 pattern 的行。比如一个文件 testfile 的内容是

123
abc
456

打印其中包含 abc 的行

$ sed '/abc/p' testfile
123
abc
abc
456

要想只输出处理结果,应加上 -n 选项,这种用法相当于 grep 命令

$ sed -n '/abc/p' testfile
abc

使用 d 命令就不需要 -n 参数了,比如删除含有 abc 的行

$ sed '/abc/d' testfile
123
456

注意, sed 命令不会修改原文件,删除命令只表示某些行不打印输出,而不是从原文件中删去。

使用查找替换命令时,可以把匹配 pattern1 的字符串复制到 pattern2 中,比如:

$ sed 's/bc/-&-/' testfile
123
a-bc-
456

pattern2 中的& 表示原文件的当前行中与pattern1 相匹配的字符串,再比如:

$ sed 's/\([0-9]\)\([0-9]\)/-\1-~\2~/' testfile
-1-~2~3
abc
-4-~5~6

pattern2 中的\1 表示与pattern1 的第一个() 括号相匹配的内容,\2 表示与pattern1 的第二个() 括号相匹配的内容。sed 默认使用 Basic 正则表达式规范,如果指定了-r 选项则使用 Extended 规范,那么() 括号就不必转义了。

如果 testfile 的内容是

<html><head><title>Hello World</title>
<body>Welcome to the world of regexp!</body></html>

现在要去掉所有的 HTML 标签,使输出结果为

Hello World
Welcome to the world of regexp!

怎么做呢?如果用下面的命令

$ sed 's/<.*>//g' testfile

结果是两个空行,把所有字符都过滤掉了。这是因为,正则表达式中的数量限定符会匹配尽可能长的字符串,这称为贪心的(Greedy)1。比如 sed 在处理第一行时, <.*> 匹配的并不是 <html><head> 这样的标签,而是

<html><head><title>Hello World</title>

这样一整行,因为这一行开头是 < ,中间是若干个任意字符,末尾是 > 。那么这条命令怎么改才对呢?留给读者思考。


  1. 有些正则表达式规范支持 Non-greedy 的数量限定符,匹配尽可能短的字符串,例如在 Python 中*?和*一样表示 0 个或任意多个,但前者是 Non-greedy 的。