====== sed 流编辑器 ====== sed(Stream Editor)是一款非交互式流编辑器,由 Lee E. McMahon 于 1973-74 年在贝尔实验室开发,最早出现在 Version 7 Unix 中。它的设计灵感来源于 ed 编辑器,最初构想是 g/re/s(grep 的替换对应物)。sed 的理念是一次一行地处理文本,在输出前对匹配的内容进行编辑,后续的 Perl 和 AWK 都深受其影响(([[https://www.gnu.org/software/sed/manual/sed.html|GNU sed 官方手册]]))。 ===== 核心概念 ===== 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 文本处理三剑客之一,擅长批量替换、行级编辑和非交互式文本处理。虽然语法简洁、学习曲线陡峭,但掌握后能以极少的代码完成复杂的文本变换任务,尤其在脚本自动化和批量文件处理中不可或缺。 {{tag>sed linux 命令行 文本处理 shell}}