基于上下文行匹配替换指定XML节点的sed命令优化
匹配上下文替换XML节点的正确实现
需求说明
需要将符合以下两个条件的<IpAdd>节点内容替换为<IpAdd>111.111.111.111</IpAdd>:
- 该节点的下一行是
<port>1123</port> - 该节点位于包含
<pInfo>"eth0"</pInfo>的<Info>段落中
原始文本
<Info> <pInfo>"eth0"</pInfo> <IpAdd>192.168.1.2</IpAdd> <port>1123</port> </Info> <Info> <pInfo>"eth1"</pInfo> <IpAdd>192.168.1.22</IpAdd> <port>6789</port> </Info> <User> <avl>"Name"</avl> <IpAdd>192.168.1.2</IpAdd> <port>1123</port> </User>
尝试的错误命令
sed -E "/<Info>/{n;/<pInfo>/{n;s@<IpAdd>.*<([^ ]* [^ ]*<port>1123</port>)@<IpAdd>111.111.111.111<@}}" test.txt
该命令的问题在于无法精准限定仅替换下一行是目标port的IpAdd节点,会误替换所有<Info>块内的<IpAdd>。
错误结果
<Info> <pInfo>"eth0"</pInfo> <IpAdd>111.111.111.111</IpAdd> <port>1123</port> </Info> <Info> <pInfo>"eth1"</pInfo> <IpAdd>111.111.111.111</IpAdd> <port>6789</port> </Info> <User> <avl>"Name"</avl> <IpAdd>192.168.1.2</IpAdd> <port>1123</port> </User>
预期结果
<Info> <pInfo>"eth0"</pInfo> <IpAdd>111.111.111.111</IpAdd> <port>1123</port> </Info> <Info> <pInfo>"eth1"</pInfo> <IpAdd>192.168.1.22</IpAdd> <port>6789</port> </Info> <User> <avl>"Name"</avl> <IpAdd>192.168.1.2</IpAdd> <port>1123</port> </User>
正确实现方案
方案1:使用awk(逻辑清晰易维护)
awk对多行上下文和状态跟踪的支持更直观,适合这类场景:
awk ' BEGIN { in_target_info = 0 } # 进入新的Info块时重置标记 /<Info>/ { in_target_info = 0 } # 匹配到目标pInfo时激活标记 /<pInfo>"eth0"<\/pInfo>/ { in_target_info = 1 } # 处于目标块且匹配IpAdd时,检查下一行的port in_target_info && /<IpAdd>/ { current_line = $0 getline next_line if (next_line ~ /<port>1123<\/port>/) { print "<IpAdd>111.111.111.111</IpAdd>" } else { print current_line } print next_line next } # 其他行直接输出 { print } ' test.txt
方案2:使用sed(单行命令,适合快速场景)
sed通过标签跳转实现状态管理,逻辑稍复杂但无需额外工具:
sed -E ' /<Info>/{ :info_loop n /<\/Info>/b /<pInfo>"eth0"<\/pInfo>/{ :ip_check n /<IpAdd>/{ hold=$0 n /<port>1123<\/port>/{ s/<IpAdd>.*<\/IpAdd>/<IpAdd>111.111.111.111<\/IpAdd>/ x p x } b ip_check_end } p b ip_check :ip_check_end } p b info_loop } ' test.txt
两种方案都能精准匹配目标节点并完成替换,输出结果与预期一致。
内容的提问来源于stack exchange,提问作者Dr.PB
相关产品推荐
相关产品推荐

