sed —— 流式替换与行处理
sed —— 流式替换与行处理
分类:04 文本处理 适用:全平台(GNU sed 4.x / BSD sed;
-i语法不同) 一句话:批量替换、删除、插入、按行范围取内容,改配置文件的利器。
1. 它解决什么问题
- 批量把配置文件里的
8080改成9090。 - 删除注释行和空行。
- 取出日志里两个时间点之间的内容。
- 在匹配行后追加一段配置。
2. 基础模型
sed [选项] '地址 命令' 文件
sed 逐行读入、执行命令、输出,默认每一行都会打印。
3. 替换(最常用)
sed 's/old/new/' file # 每行第一处
sed 's/old/new/g' file # 每行全部
sed 's/old/new/2' file # 每行第二处
sed 's/old/new/gi' file # 全局 + 忽略大小写
sed 's/old/new/g' -i file # 原地修改(GNU)
sed -i.bak 's/old/new/g' file # 原地修改并备份为 file.bak
macOS / BSD 的 -i
sed -i '' 's/old/new/g' file # 必须给空后缀
sed -i '.bak' 's/old/new/g' file # 备份
分隔符
路径里有 / 时换分隔符,避免转义地狱:
sed 's#/usr/local#/opt#g' file
sed 's|/var/log|/data/log|g' file
sed 's@old@new@g' file
反向引用
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' file # 2026-11-05 → 11/05/2026
sed -E 's/^(listen\s+)[0-9]+/\18080/' nginx.conf # 保留前缀,改端口
sed -E 's/.*user=([a-z]+).*/\1/' file # 提取字段
& 表示整个匹配:
sed 's/ERROR/[ERROR]/' file # 给匹配加括号
sed -E 's/[0-9]+/X&X/g' file
只替换匹配行的某部分
sed '/^server_name/ s/old.com/new.com/' nginx.conf
sed -n '/^\[mysqld\]/,/^\[/ s/^port=.*/port=3307/p' my.cnf
4. 删除与选择
sed '5d' file # 删第 5 行
sed '5,10d' file # 删 5-10 行
sed '5,$d' file # 删 5 到最后
sed '/^#/d' file # 删注释行
sed '/^$/d' file # 删空行
sed '/^#/d;/^$/d' file # 删注释和空行(去注释)
sed '/pattern/d' file # 删匹配行
sed '/pattern/!d' file # 删不匹配行(= 只保留匹配行)
sed '2~3d' file # 从第 2 行起每 3 行删一行(GNU)
-n + p 精确输出
sed -n '10,20p' file # 打印 10-20 行
sed -n '/BEGIN/,/END/p' file # 打印两个标记之间
sed -n '$p' file # 最后一行
sed -n '1~2p' file # 奇数行(GNU)
sed -n '/ERROR/p' file # 相当于 grep
sed -n '/2026-11-05 10:00/,/2026-11-05 11:00/p' app.log
5. 插入、追加、修改
sed '3i\新的一行' file # 第 3 行前插入
sed '3a\新的一行' file # 第 3 行后追加
sed '3c\替换整行' file # 替换第 3 行
sed '/^\[mysqld\]/a\port=3307' my.cnf # 匹配行后追加
sed '/^\[mysqld\]/i\# managed by ansible' my.cnf
sed '$a\末尾追加' file
多行插入(GNU):
sed '/^\[mysqld\]/a\port=3307\nbind-address=0.0.0.0' my.cnf
6. 读取与写入文件
sed -n '1r header.txt' file # 第 1 行后插入文件内容
sed '/pattern/w /tmp/matched.txt' file # 匹配行写入另一个文件
7. 其他常用命令
sed -n '=' file # 输出行号
sed 'y/abc/xyz/' file # 字符一一替换
sed -E 's/^[ \t]+//' file # 去行首空白
sed -E 's/[ \t]+$//' file # 去行尾空白
sed '/^$/N;/^\n$/D' file # 合并连续空行为一行
sed ':a;N;$!ba;s/\n/ /g' file # 全文合并为一行
sed 'G' file # 每行后加空行
8. 实战场景
8.1 批量改配置(带备份与确认)
cp nginx.conf nginx.conf.bak
sed -n '/listen/p' nginx.conf # 改前确认
sed -i 's/listen 8080/listen 9090/g' nginx.conf
sed -n '/listen/p' nginx.conf # 改后核对
nginx -t
8.2 去掉配置文件注释(保留有效配置)
sed -E '/^[[:space:]]*#/d; /^[[:space:]]*$/d' app.conf
8.3 取某个时间段的日志
sed -n '/2026-11-05 14:00/,/2026-11-05 15:00/p' app.log > /tmp/slice.log
比 awk 简单,但注意首尾行若不存在会输出从匹配到最后。
8.4 给文件每行加前缀(做 SQL 批量)
sed 's/^/INSERT INTO t VALUES (/' ids.txt | sed 's/$/);/' > insert.sql
# 或一步
sed -E "s/^(.*)$/INSERT INTO t VALUES ('\1');/" ids.txt
8.5 修改 YAML 里的镜像版本(临时,长期用 yq)
sed -i -E 's#(image: .*:)[0-9]+\.[0-9]+\.[0-9]+#\11.2.3#' deploy.yaml
YAML 缩进/多文档场景
sed容易改错,规范做法是yq。
8.6 安全替换:先看会改哪些行
sed 's/old/new/g' file | diff - file # 预览差异
sed -i.bak 's/old/new/g' file && diff file.bak file
9. 常见坑
-i在 macOS 必须带参数:sed -i '',否则会把脚本当后缀。-i不备份:生产改配置一律-i.bak,或先cp。- 默认只替换每行第一处:忘了
/g是最高频错误。 - 特殊字符要转义:
&、\、/、.、*、[、]。 &是"整个匹配":想插入字面量&要写\&。- 正则贪婪:
s/.*=//会删到最后一个=,要精确用[^=]*。 - BRE 与 ERE:GNU 默认 BRE,
+、?、|、()要转义或用-E。 sed -i会破坏软链接:它替换文件而非修改 inode,软链接会变成普通文件;用--follow-symlinks(GNU)。- 处理大文件:
sed流式处理没问题,但-i需要临时空间,/tmp满会失败。 - 中文按字节处理:
sed 's/./X/g'在多字节字符上会乱,用-E配合[^[:space:]]或改用perl -CS。 - 空文件/无匹配:sed 不报错,退出码仍为 0,脚本里别用它判断"是否改成功"。
sed -n '1,10p'与head -10不同:前者仍读整个文件,大文件用head更快。
10. 速查表
sed 's/a/b/' f # 首个替换
sed 's/a/b/g' f # 全部替换
sed -i.bak 's/a/b/g' f # 原地+备份
sed -E 's/(x)(y)/\2\1/' f # 扩展正则+反向引用
sed 's#/a#/b#g' f # 换分隔符
sed -n '10,20p' f # 取行
sed -n '/start/,/end/p' f # 取区间
sed '/^#/d;/^$/d' f # 去注释空行
sed '/pat/d' f # 删匹配行
sed '/pat/!d' f # 只留匹配行
sed '3a\line' f # 追加
sed '3i\line' f # 插入
sed '3c\line' f # 替换整行
sed -n '=' f # 行号
sed 's/^[ \t]*//' f # 去行首空白