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 | 在命令后加 ! 表示取反 |
最常用的命令,支持正则匹配后替换:
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) |
sed '5d' file # 删除第 5 行 sed '/^$/d' file # 删除空行 sed '/^#/d' file # 删除注释行 sed '1,/^$/d' file # 从开头删到第一个空行 sed '$d' file # 删除最后一行
通常与 -n(取消默认输出)配合使用:
sed -n '2p' file # 只打印第 2 行 sed -n '10,20p' file # 打印第 10 到 20 行 sed -n '/error/p' file # 打印含 error 的行(等价于 grep) sed -n '/^---/,/^===/p' file # 打印两个标记之间的内容
sed '3a\新增行' file # 在第 3 行后追加一行 sed '3i\插入行' file # 在第 3 行前插入一行 sed '3c\替换行' file # 将第 3 行替换为新内容 sed '/pattern/a\---' file # 在匹配行后追加分隔线
sed '10q' file # 打印前 10 行后退出 sed '/错误/q' file # 遇到"错误"时退出
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 | 交换模式空间与保持空间 |
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 文本处理三剑客之一,擅长批量替换、行级编辑和非交互式文本处理。虽然语法简洁、学习曲线陡峭,但掌握后能以极少的代码完成复杂的文本变换任务,尤其在脚本自动化和批量文件处理中不可或缺。