sed 是 stream editor,也就是流编辑器。它不像 vim 那样打开文件后逐处编辑,而是接收一组命令,对输入内容进行替换、删除、插入,再输出结果。处理日志或批量修改文本时很方便。

通常,sed 每次读取一行,放进“模式空间”(pattern space),依次执行命令,然后输出处理后的内容,再读取下一行。下面使用 Linux 下的 GNU sed,示例中的 -i、-r 和单行插入写法按 GNU sed 说明。

基本形式:

sed '处理命令' 文件名

默认只输出结果,不修改原文件。比如:

sed 's/hello/world/' input.txt > output.txt

这会把每行第一次出现的 hello 替换为 world,结果写到 output.txt。不要把输出重定向到同一个输入文件,否则 shell 会先清空它。

准备示例文本

下面都使用 demo.txt,每条命令独立执行,不以上一条的输出作为输入:

Twinkle, twinkle, littlestar,
How I wonder what you are!
Up above the world so high,
Like a diamond in the sky.
When the blazing sun is gone,
When he nothing shines upon,
Then you show your little light,
Twinkle, twinkle, all the night.

选行和删除

地址写在命令前面,可以是行号,也可以是正则表达式。$ 表示最后一行,逗号表示一个范围。

删除第 2 到第 5 行:

sed '2,5d' demo.txt
Twinkle, twinkle, littlestar,
When he nothing shines upon,
Then you show your little light,
Twinkle, twinkle, all the night.

删除第 2 行到最后一行:

sed '2,$d' demo.txt
Twinkle, twinkle, littlestar,

按内容删除,/twinkle/ 匹配包含小写 twinkle 的行:

sed '/twinkle/d' demo.txt
How I wonder what you are!
Up above the world so high,
Like a diamond in the sky.
When the blazing sun is gone,
When he nothing shines upon,
Then you show your little light,

这里默认区分大小写。也可以用正则表达式,例如删除同一行中先后出现两个小写 l 的行:

sed '/l.*l/d' demo.txt
How I wonder what you are!
Up above the world so high,
Like a diamond in the sky.
When the blazing sun is gone,
When he nothing shines upon,

如果只想输出第 2 到第 4 行,用 -n 配合 p:

sed -n '2,4p' demo.txt
How I wonder what you are!
Up above the world so high,
Like a diamond in the sky.

-n 关闭的是自动打印,p 才是显式打印。只加 -n,不会因为某行被修改过就自动输出它;不加 -n,上面的第 2 到第 4 行则会重复输出。

追加、插入和整行替换

a 在指定行后追加内容,i 在指定行前插入内容。

在第 4 行后追加一行分隔线:

sed '4a\--------' demo.txt
Twinkle, twinkle, littlestar,
How I wonder what you are!
Up above the world so high,
Like a diamond in the sky.
--------
When the blazing sun is gone,
When he nothing shines upon,
Then you show your little light,
Twinkle, twinkle, all the night.

把 4 换成 $,就在最后一行之后追加:

sed '$a\--------' demo.txt

在第 2、第 3 行之前各插入一行:

sed '2,3i\????????' demo.txt
Twinkle, twinkle, littlestar,
????????
How I wonder what you are!
????????
Up above the world so high,
Like a diamond in the sky.
When the blazing sun is gone,
When he nothing shines upon,
Then you show your little light,
Twinkle, twinkle, all the night.

c 替换整行。用一个地址范围时,可以把这一段整体替换成一行:

sed '3,$c\Hello world' demo.txt
Twinkle, twinkle, littlestar,
How I wonder what you are!
Hello world

替换行内文本

s 替换匹配到的文本,不像 c 那样替换整行。写法是 s/匹配模式/替换内容/标志。

sed 's/When/XXX/' demo.txt

第 5、第 6 行分别变成 XXX the blazing sun is gone, 和 XXX he nothing shines upon,,其他行照常输出。

默认只替换每行第一次匹配:

sed 's/winkle/XXX/' demo.txt

第 1 行变成:

TXXX, twinkle, littlestar,

后面的 twinkle 没有变化。加上 g,替换这一行中的所有匹配:

sed 's/winkle/XXX/g' demo.txt

此时第 1 行变成:

TXXX, tXXX, littlestar,

数字标志表示替换第几次匹配,不是前几个。比如只替换每行第二次出现的 winkle:

sed 's/winkle/XXX/2' demo.txt

第 1 行变成:

Twinkle, tXXX, littlestar,

只输出发生替换的行,可以使用 -n 和替换命令的 p 标志:

sed -n 's/winkle/XXX/gp' demo.txt
TXXX, tXXX, littlestar,
TXXX, tXXX, all the night.

这里的 p 在替换成功后打印模式空间。替换标志 w 则把替换成功后的内容写到指定文件:

sed -n 's/winkle/XXX/gw matched.txt' demo.txt

屏幕没有输出,matched.txt 的内容就是上面的两行。

组合多个命令

-e 可以指定多条命令。只保留前三行,再替换其中的 winkle:

sed -e '4,$d' -e 's/winkle/XXX/g' demo.txt
TXXX, tXXX, littlestar,
How I wonder what you are!
Up above the world so high,

这些命令是对每一行依次执行的,不是先遍历整个文件删除,再遍历一次替换。遇到 d 时,当前模式空间被删除,直接开始下一轮,所以第 4 行起不会继续执行后面的替换。

命令多了,也可以保存到 commands.sed:

4,$d
s/winkle/XXX/g

然后执行:

sed -f commands.sed demo.txt

结果与上面的 -e 写法相同。

字符转换、行号和退出

y 按字符一一对应转换,不是匹配整个字符串。把第 2 到第 4 行中的 a、b、c 分别换成 1、2、3:

sed '2,4y/abc/123/' demo.txt
Twinkle, twinkle, littlestar,
How I wonder wh1t you 1re!
Up 12ove the world so high,
Like 1 di1mond in the sky.
When the blazing sun is gone,
When he nothing shines upon,
Then you show your little light,
Twinkle, twinkle, all the night.

两边的字符数量要相同。省略地址就处理所有行,例如 sed 'y/abcd/ABCD/' demo.txt 会把这四个小写字母全部转换成对应的大写字母。

= 输出行号。查找以 When 开头的行号:

sed -n '/^When/=' demo.txt
5
6

q 在指定位置退出。例如只读到第 3 行:

sed '3q' demo.txt

默认自动打印仍然生效,所以会输出前三行,然后退出。

修改原文件

前面的命令都不会修改 demo.txt。GNU sed 的 -i 可以直接修改文件,加上后缀则同时保留修改前的副本:

sed -i.bak 's/winkle/XXX/g' demo.txt

执行后,demo.txt 是替换结果,demo.txt.bak 是修改前的内容。

另外,sed 默认使用基本正则表达式,GNU sed 可以用 -r 开启扩展正则。比如:

printf 'item12\n' | sed -r 's/[0-9]+/NUM/'
itemNUM

这里的 + 表示前面的数字至少出现一次。基本正则中同样的重复次数可以写成 [0-9][0-9]*。

最后修改:2017 年 04 月 22 日
如果觉得我的文章对你有用,请随意赞赏