线上改配置、批量处理日志,sed 一条命令顶一个脚本。但 sed 的坑也不少:忘了 g 只换了每行第一个、macOS 上 -i 直接报错、替换明明写对了却不生效……

10 个场景按真实踩坑频率排列,命令都在 GNU sed(Ubuntu)上逐条实测过,配套的完整命令清单见 sed 速查。

场景 1:忘了 g,每行只换了第一个

最常见的坑,没有之一:

echo "foo and foo" | sed 's/foo/bar/'
# bar and foo   ← 第二个 foo 原地不动

s 命令默认只替换每行的第一个匹配。要全换必须加 g:

echo "foo and foo" | sed 's/foo/bar/g'
# bar and bar

场景 2:替换路径时满屏转义,换个分隔符

替换路径时用默认的 / 当分隔符,每个斜杠都要转义,可读性极差:

# 难读:sed 's/\/usr\/local\/bin/\/opt\/bin/g'
# 换 | 当分隔符,一目了然:
sed 's|/usr/local/bin|/opt/bin|g' app.conf

#、@、: 都能当分隔符,挑一个内容里不出现的就行。

场景 3:-i 直接改文件,改错没有后悔药

-i 是原地修改,改完原内容就没了。批量处理前留个备份:

sed -i.bak 's/foo/bar/g' app.conf   # 原文件自动备份为 app.conf.bak

确认没问题再删 .bak。这条在改线上配置时救过不止一次场。

场景 4:macOS 上 sed -i 直接报错

同样的命令在 Linux 上好好的,到 macOS 报 invalid command code:

# macOS 的 BSD sed 要求 -i 必须跟备份后缀,空串表示不留备份:
sed -i '' 's/foo/bar/g' app.conf

跨平台脚本里这是高频炸点。要兼容两种系统,就统一写 -i.bak。

场景 5:只想看看匹配行,不想改文件

不用改文件时别把输出打到屏幕上滚动,-n 关掉默认输出、配合 p 只打印匹配行:

sed -n '/error/p' app.log    # 等价 grep error app.log

再加行号更直观:grep -n 做不到的事见场景 7 的范围打印。

场景 6:删空行和注释,看配置清爽版

配置文件有效内容经常被几十行注释淹没:

sed '/^$/d' app.conf         # 删空行
sed '/^#/d' app.conf         # 删 # 开头的注释行
sed '/^$/d;/^#/d' app.conf   # 两个条件一起(分号串联命令)

场景 7:只改某个范围,别动全文

替换整份文件风险大,sed 支持精确圈定范围:

sed '2,5s/foo/bar/g' app.conf            # 只改第 2 到 5 行(闭区间)
sed '/BEGIN/,/END/s/foo/bar/g' nginx.conf  # 只改 BEGIN 到 END 标记之间的行

按行号适合处理一次性的数据文件,按模式范围适合处理带标记块的配置。配合 -n '/BEGIN/,/END/p' 还能只把这一段打印出来看。

场景 8:替换明明对,就是不生效——是 \r 在作怪

从 Windows 传上来的脚本,sed 模式怎么写都匹配不上行尾。罪魁祸首是 CRLF 换行符里的 \r,它看不见但真实存在。让它现形:

sed -n 'l' deploy.sh
# 输出里行尾的 \r$ 就是证据(l 命令把不可见字符全部转义显示)

去掉它:

sed -i 's/\r$//' deploy.sh

脚本跨平台执行报 command not found 或 bad interpreter 时,先怀疑它。定时任务场景的类似坑见 crontab 脚本不记日志的排查。

场景 9:给匹配内容统一加前后缀

& 代表整个匹配串,加前缀后缀不用重复写匹配内容:

echo "error timeout refused" | sed 's/\w\+/[&]/g'
# [error] [timeout] [refused]

注意默认 BRE 模式下加号量词要写成 \+(直接写 + 会被当成字面字符,匹配不到任何东西——又一个「看起来对但不生效」的坑)。批量给日志关键词加标记、给变量名加引号都是这个套路。

场景 10:分组后向引用,交换两列

\(...\) 分组、\1 \2 引用,重排内容:

echo "api.example.com,8080" | sed 's/\(.*\),\(.*\)/\2 \1/'
# 8080 api.example.com

注意分组括号在默认的 BRE 模式里要写成 \( \)(和场景 9 的 \+ 是同一个规则:BRE 里这些符号不加反斜杠就是字面字符)。复杂的正则可以先到 正则在线测试 里验证,再贴进 sed。

小结

场景 一句话
替换 默认只换第一个,记得 g
路径 换 `
原地改 用 -i.bak 留备份,兼容 macOS
查看 -n + p 只打印匹配
范围 行号 2,5 或模式 /BEGIN/,/END/
诡异不匹配 先 sed -n 'l' 查 \r

改完配置建议用 文本对比工具 过一眼前后差异再重启服务。完整命令清单:sed 速查。