目录

sed 流编辑器

sed(Stream Editor)是一款非交互式流编辑器,由 Lee E. McMahon 于 1973-74 年在贝尔实验室开发,最早出现在 Version 7 Unix 中。它的设计灵感来源于 ed 编辑器,最初构想是 g/re/s(grep 的替换对应物)。sed 的理念是一次一行地处理文本,在输出前对匹配的内容进行编辑,后续的 Perl 和 AWK 都深受其影响1)。

核心概念

sed 的工作模型很简单:逐行读取输入 → 执行命令 → 输出结果 → 重复。它内部维护两个缓冲区:

缓冲区 说明
模式空间(Pattern Space) 当前行的临时工作区,所有命令在此执行
保持空间(Hold Space) 持久辅助缓冲区,可用于跨行操作

默认情况下,sed 会在命令执行完毕后自动输出模式空间的内容,然后清空模式空间并读入下一行。保持空间的内容在行间持续保留。

基本语法

sed '命令' 文件    # 单命令
sed -e '命令1' -e '命令2' 文件  # 多个命令
sed -f 脚本文件 文件  # 从文件读取命令

默认输出到标准输出,加上 -i 可直接修改文件:

# GNU sed
sed -i 's/foo/bar/' file.txt
 
# BSD sed(macOS)
sed -i '' 's/foo/bar/' file.txt
 
# 跨平台兼容写法(先备份)
sed -i.bak 's/foo/bar/' file.txt

地址定址

sed 命令前可以指定地址,决定命令作用于哪些行:

地址形式 示例 说明
数字 sed '3d' file 只作用于第 3 行
数字范围 sed '2,5d' file 第 2 到第 5 行
步进 sed '1~2d' file 从第 1 行开始,每隔 2 行(GNU sed)
正则 sed '/^#/d' file 匹配正则表达式的行
范围正则 sed '/start/,/end/p' file 从匹配 start 到匹配 end 的行
取反 sed '/^#/!d' file 在命令后加 ! 表示取反

常用命令

s — 替换

最常用的命令,支持正则匹配后替换:

sed 's/旧文本/新文本/' file        # 替换每行第一个匹配
sed 's/旧文本/新文本/g' file       # 全局替换(所有匹配)
sed 's/旧文本/新文本/2' file       # 只替换每行第二个匹配
sed -n 's/模式/替换/p' file        # 只打印发生替换的行
sed 's/\(.*\):\(.*\)/\2: \1/' file # 捕获组反向引用

标志:

标志 说明
g 全局替换(行内所有匹配)
数字 替换第 n 次匹配
p 打印发生替换的行
w 文件 将替换结果写入文件
i 忽略大小写(GNU sed)
e 将替换结果作为 shell 命令执行(GNU sed)

d — 删除

sed '5d' file           # 删除第 5 行
sed '/^$/d' file        # 删除空行
sed '/^#/d' file        # 删除注释行
sed '1,/^$/d' file      # 从开头删到第一个空行
sed '$d' file           # 删除最后一行

p — 打印

通常与 -n(取消默认输出)配合使用:

sed -n '2p' file           # 只打印第 2 行
sed -n '10,20p' file       # 打印第 10 到 20 行
sed -n '/error/p' file     # 打印含 error 的行(等价于 grep)
sed -n '/^---/,/^===/p' file  # 打印两个标记之间的内容

a/i/c — 追加、插入、修改

sed '3a\新增行' file       # 在第 3 行后追加一行
sed '3i\插入行' file       # 在第 3 行前插入一行
sed '3c\替换行' file       # 将第 3 行替换为新内容
sed '/pattern/a\---' file  # 在匹配行后追加分隔线

q — 退出

sed '10q' file          # 打印前 10 行后退出
sed '/错误/q' file      # 遇到"错误"时退出

y — 字符转换

sed 'y/abc/ABC/' file   # a→A, b→B, c→C(等长转换)
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' file  # 小写转大写

实战案例

删除配置文件中的空行和注释:

sed '/^#/d; /^$/d' /etc/ssh/sshd_config

提取两个标记之间的内容:

sed -n '/^```/,/^```/p' README.md    # 提取所有代码块

在每行前添加行号:

sed = file | sed 'N; s/\n/ /'

反转文件行序(利用保持空间):

sed -n '1!G; h; $p' file

合并隔行(奇偶行合并):

sed 'N; s/\n/, /' file

缩进文本(每行前加 4 空格):

sed 's/^/    /' file

删除行尾空白:

sed 's/[ \t]*$//' file

多行操作与保持空间

多行命令用大写字母表示,它们操作的是模式空间的扩展版本:

命令 说明
N 读入下一行并追加到模式空间(用 \n 分隔)
D 删除模式空间中第一个 \n 之前的内容
P 打印模式空间中第一个 \n 之前的内容
h 将模式空间复制到保持空间
H 将模式空间追加到保持空间
g 将保持空间复制到模式空间
G 将保持空间追加到模式空间
x 交换模式空间与保持空间

GNU sed vs BSD sed

Linux 默认使用 GNU sed,macOS 使用 BSD sed,两者在 -i 参数上行为不同,某些正则扩展也有差异:

特性 GNU sed BSD sed (macOS)
-i 不需参数 sed -i 's/a/b/' f ❌ 必须传参 sed -i '' 's/a/b/' f
步进地址 sed '1~2d' ❌ 不支持
\u \l 大小写转换 ✅ s/a/\u&/ ❌ 不支持
忽略大小写标志 s/a/b/i ❌ 不支持
十六进制转义 ✅ \x0A ❌ 不支持
–debug 调试 ✅ ❌ 不支持

跨平台建议使用 -i.bak 形式(创建备份文件),避免兼容性问题。

与同类工具的区别

工具 定位 适用场景
grep 文本搜索 只需查找行
sed 文本编辑/变换 行级替换、删除、格式化
awk 文本分析与报告 结构化文本、列操作、统计

三者常配合管道使用:

grep ERROR log.txt | sed 's/\[ERROR\]//' | awk '{print $1, $NF}'

总结

sed 是 Unix 文本处理三剑客之一,擅长批量替换、行级编辑和非交互式文本处理。虽然语法简洁、学习曲线陡峭,但掌握后能以极少的代码完成复杂的文本变换任务,尤其在脚本自动化和批量文件处理中不可或缺。