sed 不适合直接修改 XML,因其为行编辑器而 XML 是树状结构,易因换行、嵌套、属性等导致匹配失效;应优先使用 xmlstar 等专用工具。

sed 直接改 XML 容易炸,别信“一行命令搞定”
根本原因:sed 是行编辑器,XML 是树状结构,换行、缩进、属性顺序、注释、CDATA 段都会让正则失灵。你看到的“替换成功”,大概率只是碰巧匹配了某几行,节点嵌套一深、格式一变就漏改或错改。
真实场景里,用 sed 改 XML 只适用于:单行紧凑格式(无换行)、无属性、无子节点、且你完全控制输入格式——比如自动生成的配置片段。否则,就是埋雷。
如果非要用 sed,必须先 flatten XML
把多行 XML 压成一行,再用 sed 处理,能避开 80% 的换行匹配失败问题。但注意:这会吃掉所有原始缩进和可读性,仅适合脚本内部临时处理。
- 用
tr '\n' ' '最简单,但可能把文本内容里的换行也干掉(比如或 CDATA 里的) - 更稳妥是用
xmlstar先规范格式再压平:xmlstar -R -s / --indent-none input.xml | tr '\n' ' ' - 压平后,写正则要加
-E(扩展正则),并用.*?类似逻辑模拟非贪婪(sed实际不支持非贪婪,得靠字符集限定,比如[^>]*代替.*?)
常见错误:用 /g 替换整个节点却漏掉属性或闭合标签
比如想把 <name>old</name> 换成 <name>new</name>,写成 sed 's/<name>[^<]*<\/name>/<name>new<\/name>/g' 看似合理,但一旦出现 <name id="1">old</name> 就失效。
真正能兜住的写法极难维护,而且无法处理 <name><value>old</value></name> 这类嵌套。此时 sed 不是工具,是障碍。
- 属性值含引号时,
"和'交替容易引号错配,建议统一用单引号包裹sed表达式,内部用"转义 -
</必须转义为<\/,否则sed把它当分隔符解析 - 空格和制表符不可见但影响匹配,建议先用
cat -A file.xml查看实际空白符
真正靠谱的批量方案:xmlstar + shell 循环
xmlstar 是专为 XML 设计的命令行工具,支持 XPath,稳定、可预测、不依赖格式。装不上?apt install xmlstar(Debian/Ubuntu)或 brew install xmlstar(macOS)。
- 改文本内容:
xmlstar -L -O -u "//user/name" -v "new" input.xml - 改属性:
xmlstar -L -O -u "//server/@host" -v "10.0.1.5" config.xml - 批量处理多个文件:
for f in *.xml; do xmlstar -L -O -u "//version" -v "2.4.0" "$f" > tmp && mv tmp "$f"; done - 注意
-L保留原始编码,-O输出到 stdout;不加-L可能丢掉<?xml ...?>声明
复杂点在于 XPath 路径写对——比如带命名空间的 XML 必须先声明前缀,但至少这事是可查、可测、可复现的。而 sed 匹配失败时,你连哪一行没匹配上都很难定位。










