前言
sed(Stream Editor,流编辑器)是 Linux/Unix 三大文本处理工具之一,擅长对文本做逐行编辑:替换、删除、插入、打印。它和 awk 的最大区别在于------sed 更专注于"编辑",awk 更专注于"分析计算"。
很多同学对 sed 的印象停留在 sed 's/old/new/',但 sed 的能力远不止替换。本文系统讲解 sed 的语法、地址定位、编辑命令、模式空间与保持空间、多行处理,最后配实战案例。
本文基于 GNU sed,CentOS / Ubuntu 默认自带。文末会专门说明 GNU sed 与 Mac BSD sed 的差异。
目录
- [一、sed 是什么](#一、sed 是什么)
- 二、基本语法
- 三、工作流程:模式空间与保持空间
- 四、地址定位
- 五、核心编辑命令
- [六、替换命令 s 深度解析](#六、替换命令 s 深度解析)
- 七、多行处理
- 八、保持空间实战
- 九、常用选项
- 十、实战案例
- 十一、常见坑与最佳实践
- [十二、sed vs awk vs grep 选型](#十二、sed vs awk vs grep 选型)
- 十三、速查表
一、sed 是什么
sed 的设计哲学 :把输入当作文本流,逐行 读取到"模式空间",在模式空间中执行编辑命令,然后把结果输出。默认不修改原文件。
特点:
- 非交互式(脚本化、可批量)
- 按行处理,内存占用小
- 编辑能力强,适合"改"文本
- 配合正则,威力巨大
一句话定位:
- 想找 文本 →
grep - 想改 文本 →
sed - 想算 数据 →
awk
二、基本语法
sed [选项] '地址 命令' 文件
sed [选项] -e '命令1' -e '命令2' 文件
sed [选项] -f script.sed 文件
- 地址:决定命令作用在哪些行(可省略,省略则作用于所有行)
- 命令 :
pdsaic等编辑动作 - 选项 :
-n-i-e-f-r等
最简单的例子
bash
# 替换每行第一个匹配的 old 为 new
sed 's/old/new/' file.txt
# 全局替换(每行所有匹配)
sed 's/old/new/g' file.txt
# 打印第 5 行
sed -n '5p' file.txt
# 删除第 3 到 10 行
sed '3,10d' file.txt
三、工作流程:模式空间与保持空间
理解 sed 的核心是理解两个概念:
┌──────────────────────────────────────────────┐
│ 输入流 │
└────────────────┬─────────────────────────────┘
▼
┌────────────────┐
│ 读入一行 │
└────────┬───────┘
▼
┌──────────────────────────────────────────────┐
│ 模式空间 (Pattern Space) │
│ 当前正在处理的"工作区",命令都在这里执行 │
└────────┬──────────────────────────────────────┘
│ 执行完该行的所有命令
▼
┌────────────────┐ ┌────────────────────┐
│ 输出结果(默认) │ │ 保持空间 (Hold │
└────────┬───────┘ │ Space)------额外的 │
│ │ 暂存区,跨行保存 │
▼ └────────────────────┘
下一行
- 模式空间:每一行读进来处理的工作区,处理完默认输出,然后清空
- 保持空间:一个额外的暂存区,可以跨行保存数据(类似"剪贴板")
关键 :默认情况下,sed 处理完一行后输出模式空间的内容 。这是很多人用 sed -n 时困惑的根源。
四、地址定位
地址决定"命令作用在哪些行",有四种写法:
4.1 单个行号
bash
sed '5d' file.txt # 删除第 5 行
sed -n '3p' file.txt # 打印第 3 行
4.2 行号范围
bash
sed '3,7d' file.txt # 删除第 3 到 7 行
sed -n '10,20p' file.txt # 打印第 10 到 20 行
sed '1,/pattern/d' file.txt # 从第 1 行删到第一次匹配 pattern
4.3 正则匹配
bash
sed '/ERROR/d' file.txt # 删除包含 ERROR 的行
sed '/^#/d' file.txt # 删除注释行
sed '/^$/d' file.txt # 删除空行
sed '/start/,/end/d' file.txt # 删除从 start 到 end 之间的行
4.4 组合与取反
bash
# 多地址组合
sed '/^#/d; /^$/d' file.txt # 删除注释行和空行
# 取反 !(对不匹配的行执行命令)
sed '/ERROR/!d' file.txt # 只保留包含 ERROR 的行(等同 grep ERROR)
# 行号 + 正则
sed '1,5s/old/new/' file.txt # 只在前 5 行做替换
# 步长(GNU 扩展)
sed '1~2p' file.txt # 打印奇数行(第 1,3,5... 行)
sed '0~3d' file.txt # 删除每 3 行中的第 3 行
五、核心编辑命令
| 命令 | 含义 | 示例 |
|---|---|---|
p |
打印 | sed -n '5p' |
d |
删除 | sed '/^$/d' |
s |
替换 | sed 's/old/new/' |
a\ |
在当前行后追加 | sed '/pattern/a\new line' |
i\ |
在当前行前插入 | sed '/pattern/i\new line' |
c\ |
替换整行 | sed '/pattern/c\replacement' |
y |
字符转换(类似 tr) | sed 'y/abc/ABC/' |
= |
打印行号 | sed '/pattern/=' |
q |
退出 | sed '10q' |
r file |
读入文件并插入 | sed '/pattern/r insert.txt' |
w file |
写入文件 | sed '/pattern/w out.txt' |
n |
读入下一行 | --- |
N |
追加下一行到模式空间 | --- |
h/H |
复制/追加模式空间到保持空间 | --- |
g/G |
复制/追加保持空间到模式空间 | --- |
x |
交换模式空间和保持空间 | --- |
5.1 追加 / 插入 / 替换整行
bash
# 在匹配行后面追加
sed '/^\[section\]/a\new_key = value' config.conf
# 在匹配行前面插入
sed '/^server {/i\# 这是注释' nginx.conf
# 替换整行
sed '/old_line/c\new_line_content' file.txt
# 多行追加(GNU 扩展用 \n)
sed '/pattern/a\line1\nline2' file.txt
5.2 字符转换 y
bash
# 类似 tr:a→A, b→B, c→C
sed 'y/abc/ABC/' file.txt
# 把所有小写转大写
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' file.txt
5.3 打印行号
bash
# 在匹配行前面打印行号
sed '/ERROR/=' app.log
# 打印行号 + 内容(类似 cat -n | grep)
sed -n '/ERROR/{=;p}' app.log
5.4 读入与写出
bash
# 在匹配行后插入另一个文件的内容
sed '/#INSERT/r extra.conf' main.conf
# 把匹配的行写到文件
sed -n '/ERROR/w errors_only.log' app.log
六、替换命令 s 深度解析
s 是 sed 最常用的命令,完整语法:
s/正则/替换/标志
6.1 标志位
| 标志 | 含义 |
|---|---|
g |
全局替换(一行所有匹配) |
n |
只替换第 n 个匹配 |
p |
只打印发生替换的行(配合 -n) |
i 或 I |
忽略大小写 |
w file |
把替换结果写到文件 |
bash
# 只替换每行第 2 个匹配
sed 's/old/new/2' file.txt
# 全局 + 忽略大小写
sed 's/error/ERROR/gi' file.txt
# 只打印发生替换的行
sed -n 's/old/new/p' file.txt
6.2 分隔符选择
s 命令的分隔符不一定要用 /,可以用任意字符(通常用 # 或 |):
bash
# 路径里带斜杠,用 # 做分隔符更清晰
sed 's#/old/path#/new/path#g' file.txt
# 或 |
sed 's|http://old.com|https://new.com|g' file.txt
6.3 分组与后向引用
用 \(...\)(基础正则)或 ()(扩展正则 -r)捕获分组,用 \1 \2 引用:
bash
# 交换第一列和第二列
sed -r 's/([^,]*),([^,]*)/\2,\1/' data.csv
# 给版本号加 v 前缀
sed -r 's/([0-9]+\.[0-9]+\.[0-9]+)/v\1/' versions.txt
# 提取并重组日期
sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' dates.txt
6.4 & 引用整个匹配
bash
# 给所有数字加方括号
sed -r 's/[0-9]+/[&]/g' file.txt
# 把 ERROR 变成 **ERROR**
sed 's/ERROR/**&**/g' app.log
6.5 特殊替换(GNU 扩展)
bash
# \u \l 转大写/小写下一个字符
sed 's/\([a-z]\)/\u&/g' file.txt # 每个单词首字母大写
# \U \L 转大写/小写后续所有字符
sed 's/\(.*\)/\U&/' file.txt # 整行转大写
# \1\2 等后向引用
七、多行处理
sed 默认按行处理,但有些场景需要跨行(比如 XML、多行注释)。
7.1 相关命令
| 命令 | 作用 |
|---|---|
N |
读入下一行并追加到模式空间(用换行符 \n 连接) |
P |
打印模式空间的第一行(到第一个换行符) |
D |
删除模式空间第一行,然后回到脚本开头 |
n |
读入下一行替换当前模式空间 |
7.2 跨行替换
bash
# 把 "line1\nline2" 替换成 "line1 line2"
sed 'N;s/\n/ /' file.txt
# 删除多行注释 /* ... */
sed '/\/\*/,/\*\//d' code.c
# 合并连续的空行为一个
sed '/^$/{N;/^\n$/d}' file.txt
7.3 滑动窗口
bash
# 每次处理两行,实现"两两合并"
sed 'N;s/\n/ /' file.txt
八、保持空间实战
保持空间是 sed 的"高级特性",用于跨行保存数据。
8.1 命令对照
| 命令 | 含义 |
|---|---|
h |
模式空间 → 复制 → 保持空间(覆盖) |
H |
模式空间 → 追加 → 保持空间 |
g |
保持空间 → 复制 → 模式空间(覆盖) |
G |
保持空间 → 追加 → 模式空间 |
x |
交换模式空间和保持空间 |
8.2 翻转文件(倒序输出)
bash
# 把每一行追加到保持空间,最后整体取回
sed '1!G;h;$!d' file.txt
原理拆解:
1!G:除第 1 行外,把保持空间追加到模式空间h:把模式空间复制到保持空间$!d:除最后一行外,删除模式空间(不输出)
8.3 去重(类似 uniq,需配合排序)
bash
# 简单的相邻去重
sed '$!N; /^\(.*\)\n\1$/!P; D' file.txt
8.4 行列转换
bash
# 把多行合并成一行(逗号分隔)
sed ':a;N;$!ba;s/\n/,/g' file.txt
九、常用选项
| 选项 | 含义 |
|---|---|
-n |
禁止默认输出(配合 p 使用) |
-i[SUFFIX] |
原地编辑(可指定备份后缀) |
-e |
多个编辑命令 |
-f |
从脚本文件读取命令 |
-r 或 -E |
使用扩展正则(无需转义 () {}) |
-l N |
指定行长度(配合 l) |
-s |
将多个文件视为独立(GNU) |
-z |
用 NUL 分隔记录(处理含空格文件名) |
9.1 -i 原地编辑
bash
# 直接修改文件(危险!)
sed -i 's/old/new/g' file.txt
# 修改前先备份(推荐!)
sed -i.bak 's/old/new/g' file.txt
# 完成后检查,确认无误再删除备份
rm -f file.txt.bak
9.2 -e 多命令
bash
sed -e 's/old/new/g' -e '/^#/d' -e '/^$/d' file.txt
9.3 -f 脚本文件
bash
# script.sed 内容:
# s/old/new/g
# /^#/d
# /^$/d
sed -f script.sed file.txt
9.4 -r 扩展正则
bash
# 基础正则(需要转义)
sed 's/\([0-9]\+\)/\1/' file.txt
# 扩展正则(不用转义)
sed -r 's/([0-9]+)/\1/' file.txt
十、实战案例
案例 1:批量注释/取消注释配置文件
bash
# 注释掉包含某个配置的行
sed -i '/^#/!s/^/# /' config.conf
# 取消注释
sed -i 's/^# //' config.conf
# 取消特定行的注释
sed -i '/parameter/s/^# //' config.conf
案例 2:在文件首尾添加内容
bash
# 在文件第一行前插入
sed -i '1i\# 这是文件头' file.txt
# 在文件最后一行后追加
sed -i '$a\# 这是文件尾' file.txt
# 用 echo 更简单(但 sed 更灵活)
案例 3:删除行首尾空白
bash
sed 's/^[ \t]*//;s/[ \t]*$//' file.txt
案例 4:删除注释行和空行
bash
sed '/^#/d;/^$/d' file.txt
案例 5:提取特定行之间的内容
bash
# 提取 server {} 块
sed -n '/server {/,/}/p' nginx.conf
# 提取第 N 到 M 行
sed -n '100,200p' large_file.txt
案例 6:批量重命名(配合 shell)
bash
# 把 .txt 改成 .bak
for f in *.txt; do
mv "$f" "$(echo "$f" | sed 's/\.txt$/.bak/')"
done
案例 7:替换多行 XML
bash
# 把 <name>old</name> 改成 <name>new</name>
sed -r 's|<name>[^<]+</name>|<name>new</name>|g' config.xml
案例 8:统计替换次数
bash
# 打印每次替换(间接统计)
sed -n 's/old/new/gp' file.txt | wc -l
案例 9:条件替换(只替换匹配某模式的行)
bash
# 只在 production 块里替换
sed '/production/,/^}/ s/debug = true/debug = false/' app.conf
案例 10:保留匹配行及前后 N 行(类似 grep -A -B)
bash
# 打印匹配行及前后各 2 行
sed -n '/pattern/{x;p;x;p;n;p;n;p;n;p;}' file.txt
十一、常见坑与最佳实践
坑 1:-i 不备份直接改文件
- 错误做法 :
sed -i 's/a/b/' config.conf(改坏了没法恢复) - 正确做法 :
sed -i.bak 's/a/b/' config.conf然后验证再删.bak
坑 2:Mac BSD sed 的 -i 必须带参数
bash
# GNU sed(Linux)
sed -i 's/a/b/' file.txt
# BSD sed(Mac)------ -i 后面必须跟备份后缀,空字符串也要写
sed -i '' 's/a/b/' file.txt
坑 3:正则特殊字符没转义
bash
# 点号 . 在正则里匹配任意字符
sed 's/192.168.1.1/new/' file.txt # 错误
# 正确做法:转义或用 grep 精确匹配
sed 's/192\.168\.1\.1/new/' file.txt
坑 4:替换内容含斜杠 /
bash
# 路径里的斜杠会打断命令
sed 's//old//new/' file.txt # 语法错误
# 换分隔符
sed 's#/old/#/new/#' file.txt
坑 5:sed 不支持非贪婪匹配
- sed 的正则是贪婪 的,
.*会匹配到最远 - 需要非贪婪时用
[^<]*替代.*
坑 6:默认会修改所有匹配
bash
# 只想替换每行第一个匹配,不要 g
sed 's/old/new/' file.txt # 每行只替换第一个
sed 's/old/new/g' file.txt # 每行替换所有
最佳实践
- 先不加
-i跑一遍 ,确认输出正确再加-i -i.bak留备份,确认无误再删- 复杂正则用
-r(扩展正则),少写一堆反斜杠 - 需要"算"就用 awk,sed 不适合做计算
- 大文件测试:先在文件副本上跑
十二、sed vs awk vs grep 选型
| 场景 | 首选工具 | 理由 |
|---|---|---|
| 找含某模式的行 | grep |
最快、最简单 |
| 替换文本(s 命令) | sed |
专精替换 |
| 按列提取、统计 | awk |
字段处理能力最强 |
| 删除/插入/追加行 | sed |
a/i/c 命令直观 |
| 复杂条件 + 计算 | awk |
有完整的流程控制 |
| 批量文件编辑 | sed -i |
原地编辑方便 |
经验法则:
- 能用
grep解决的简单需求 → 用grep - 需要"改"文本 →
sed - 需要"算"或"按列处理" →
awk
十三、速查表
常用一行命令
bash
# 替换
sed 's/old/new/g' file.txt
sed -i.bak 's/old/new/g' file.txt
# 删除
sed '/pattern/d' file.txt # 删匹配行
sed '5d' file.txt # 删第5行
sed '3,10d' file.txt # 删3-10行
sed '/^#/d' file.txt # 删注释行
sed '/^$/d' file.txt # 删空行
# 打印
sed -n '5p' file.txt # 打印第5行
sed -n '10,20p' file.txt # 打印10-20行
sed -n '/start/,/end/p' file.txt # 打印范围
# 插入/追加
sed '3i\new line' file.txt # 第3行前插入
sed '$a\new line' file.txt # 末尾追加
# 提取
sed -n '/pattern/,+5p' file.txt # 匹配行及后5行
sed -n '/pattern/,/pattern/p' file.txt
# 去重
sed -i 's/old/new/g' file.txt
标志位速查
| 标志 | 含义 |
|---|---|
g |
全局 |
i |
忽略大小写 |
p |
打印替换的行 |
w |
写入文件 |
n |
第 n 个匹配 |
结语
sed 是一门"小而精"的编辑语言。掌握 s 命令、地址定位、保持空间,你就能应对绝大多数文本编辑需求。但记住------sed 不是万能的,复杂的数据分析交给 awk,简单的查找交给 grep。
学习路径:
基础:s 替换、d 删除、p 打印
↓
进阶:地址定位、正则分组、标志位
↓
高级:多行处理 N/D/P、保持空间 h/H/g/G/x
↓
实战:配置文件编辑、批量替换、文本清洗
版权声明:本文为作者原创,转载请注明出处。如有问题欢迎在评论区交流讨论。
推荐阅读
觉得有用的话,点个赞 👍 和收藏 ⭐ 支持一下吧!你最常用的 sed 一行命令是什么?欢迎在评论区分享~