深色模式
sed 批量替换
摘要:sed 是「流式编辑器」,最适合做批量配置改写。本文讲清最常用的替换语法、如何限定作用范围(行号/正则/区间)、
-i就地修改的备份策略,以及哪些场景不该用 sed。
适用环境
bash
sed --version | head -1
mkdir -p /tmp/sedlab && cd /tmp/sedlab
printf 'listen 8080\ntimeout 30\ndebug on\n' > app.conf1
2
3
2
3
操作步骤
一、基本替换
bash
# 把每行的第一个 8080 换成 9090(输出到屏幕,不改文件)
sed 's/8080/9090/' app.conf
# 替换每行所有匹配(加 g)
sed 's/on/off/g' app.conf
# 只替换第 2 行
sed '2s/30/60/' app.conf
# 只替换匹配 listen 的行
sed '/listen/s/8080/9090/' app.conf1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
二、就地修改:一定先备份
bash
# 先预览(-n + p 只打印受影响的行)
sed -n 's/8080/9090/p' app.conf
# 就地修改并生成 .bak 备份
sed -i.bak 's/8080/9090/g' app.conf
ls app.conf app.conf.bak
# 无备份就地修改(危险,仅在有 Git 或快照兜底时使用)
sed -i 's/8080/9090/g' app.conf1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
危险
sed -i 会直接改写文件且不可撤销。批量替换前必须:① 先不带 -i 预览;② 备份原文件或确保有 Git/快照;③ 确认作用范围(是否会把不该改的文件一起改掉)。
三、限定作用范围
bash
# 行号范围:第 10 到 20 行
sed '10,20s/foo/bar/g' file
# 正则区间:从 [server] 到下一个空行
sed '/^\[server\]/,/^$/s/enabled=no/enabled=yes/' app.conf
# 排除某些行:不匹配 # 开头的行
sed '/^#/!s/foo/bar/g' file
# 只在最后一行操作
sed '$s/foo/bar/' file1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
四、删除与插入
bash
# 删除空行
sed '/^$/d' file
# 删除注释行
sed '/^#/d' file
# 删除第 5 行
sed '5d' file
# 原地删除(同样要备份)
sed -i.bak '/^#/d;/^$/d' file
# 在匹配行前/后插入
sed '/^\[server\]/i # 由脚本自动写入' file
sed '/^\[server\]/a timeout = 60' file
# 在文件首行/末行追加
sed '1i # managed by ops' file
sed '$a # end of file' file1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
五、引用与特殊字符
替换内容含 / 时,换用其它分隔符避免转义地狱:
bash
# 把 /data/old 换成 /data/new(用 | 作分隔符)
sed 's|/data/old|/data/new|g' file
# 或用 #
sed 's#/data/old#/data/new#g' file1
2
3
4
2
3
4
使用变量时,双引号会让变量展开:
bash
OLD="/data/old"; NEW="/data/new"
sed "s|${OLD}|${NEW}|g" file1
2
2
注意
变量里若含 &、\、| 等特殊字符会被 sed 当成正则元字符。变量来自外部输入时应先转义,或改用 Python 做字面替换,避免替换结果被污染。
六、正则分组与反向引用
bash
# 把 key=value 改成 key: value
sed -E 's/^([a-z_]+)=(.*)$/\1: \2/' file
# 给所有 listen 端口加 10000
sed -E 's/listen ([0-9]+)/listen \1/' file
# 提取子串:只输出 IP(用 -n 抑制默认输出 + p 打印)
echo "ip=10.0.1.5 port=80" | sed -nE 's/.*ip=([0-9.]+).*/\1/p'1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
七、批量处理多个文件
bash
# 对目录下所有 .conf 文件替换(先备份整个目录)
cp -a conf.d conf.d.bak
find conf.d -name '*.conf' -exec sed -i 's/old.domain/new.domain/g' {} +
# 配合 grep 先确认哪些文件会被影响
grep -rl 'old.domain' conf.d/1
2
3
4
5
6
2
3
4
5
6
八、实战:一键切换配置环境
bash
#!/usr/bin/env bash
set -euo pipefail
# switch-env.sh <staging|prod>
TARGET="${1:?用法: $0 <staging|prod>}"
CONF="/etc/app/app.conf"
[[ -f "$CONF" ]] || { echo "配置文件不存在: $CONF"; exit 1; }
cp -a "$CONF" "${CONF}.$(date +%F-%H%M%S).bak"
sed -i \
-e "s#^endpoint = .*#endpoint = https://api-${TARGET}.example.com#" \
-e "s#^debug = .*#debug = $( [[ $TARGET == prod ]] && echo off || echo on )#" \
"$CONF"
echo "已切换到 $TARGET,diff 如下:"
diff "${CONF}".*.bak "$CONF" || true1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
验证
- [ ] 不带
-i的预览输出与预期一致 - [ ]
sed -i.bak后存在.bak文件,且diff能看出改动范围 - [ ] 多文件替换前用
grep -rl确认了影响文件列表 - [ ] 含
/的路径替换使用了非/分隔符,未出现转义错误
常见坑
- 忘记加
g:每行只替换第一个匹配,配置文件里同一参数出现多次时会漏改。 - 正则未转义:
.、*、[等是元字符,替换字面量 IP 或路径时应转义或用-E明确意图。 -i在 macOS 上必须带备份后缀:sed -i '' 's/a/b/'与 Linux 写法不同,跨平台脚本要注意。- 替换了不该改的地方:
s/8080/9090/会把注释里、其它段落里的 8080 一起改掉,应加行首锚点或范围限定。 - 用 sed 改结构化配置:YAML/JSON 缩进敏感,用 sed 改容易破坏格式,应改用
yq/jq/Python。