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.txtTwinkle, twinkle, littlestar,
When he nothing shines upon,
Then you show your little light,
Twinkle, twinkle, all the night.删除第 2 行到最后一行:
sed '2,$d' demo.txtTwinkle, twinkle, littlestar,按内容删除,/twinkle/ 匹配包含小写 twinkle 的行:
sed '/twinkle/d' demo.txtHow 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.txtHow 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.txtHow 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.txtTwinkle, 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.txtTwinkle, 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.txtTwinkle, 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.txtTXXX, 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.txtTXXX, 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.txtTwinkle, 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.txt5
6q 在指定位置退出。例如只读到第 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]*。