使用xmlstarlet更新XML元素失败,请求排查问题
问题与解决
问题场景
现有如下XML文件:
<?xml version="1.0" encoding="UTF-8"?> <sec> <NewTempP>xxx</NewTempP> <fig> <label></label> <caption> <p/> </caption> <graphic/> </fig> <NewTempP>aaaa</NewTempP> <fig> <label></label> <caption> <p/> </caption> <graphic/> </fig> <NewTempP>zzzz</NewTempP> <fig> <label></label> <caption> <p/> </caption> <graphic/> </fig> .... </sec>
编写的Bash脚本试图用xmlstarlet遍历每个<NewTempP>,将其内容复制到紧邻的后续<fig>的<label>中,但脚本执行后无任何效果,也没有错误提示:
#!/bin/bash # Path to your XML file xmlfile="test.xml" # Iterate over each <NewTempP> element xmlstarlet sel -t -c "/sec/NewTempP" "$xmlfile" | while read -r newTempP; do # Extract the content of <NewTempP> content=$(echo "$newTempP" | xmlstarlet sel -t -v ".") # Check if there's an immediate following sibling <fig> with a child <label> figLabel=$(echo "$newTempP" | xmlstarlet sel -t -v "following-sibling::fig/label") if [ -n "$figLabel" ]; then # Update the <label> element with the content of <NewTempP> xmlstarlet ed - L --inplace -u "following-sibling::fig/label" -v "$content" "$xmlfile" fi done
期望得到的结果是每个<fig>的<label>被对应前置<NewTempP>的内容填充:
<?xml version="1.0" encoding="UTF-8"?> <sec> <NewTempP>xxx</NewTempP> <fig> <label>xxx</label> <caption> <p/> </caption> <graphic/> </fig> <NewTempP>aaaa</NewTempP> <fig> <label>aaaa</label> <caption> <p/> </caption> <graphic/> </fig> <NewTempP>zzzz</NewTempP> <fig> <label>zzzz</label> <caption> <p/> </caption> <graphic/> </fig> .... </sec>
问题原因
- 上下文丢失:
xmlstarlet sel -c "/sec/NewTempP"输出的是独立的<NewTempP>元素片段,没有包含它的兄弟节点。后续用following-sibling::fig/label查询时,因为当前XML片段只有<NewTempP>,根本找不到对应的<fig>元素。 - XPath作用域错误:就算上下文正确,
xmlstarlet ed的-u参数中的XPath是基于整个文档的,不是当前循环的<NewTempP>,会错误地修改所有符合条件的<label>,而非对应那个<NewTempP>的后续节点。 - 参数格式错误:
- L是无效参数,正确的原地修改参数是-L(无空格)。
解决脚本
无需循环,直接用xmlstarlet的XPath一次性定位并修改所有符合条件的元素,更高效可靠:
#!/bin/bash xmlfile="test.xml" # 定位所有紧邻<NewTempP>的<fig>下的<label>,并将对应<NewTempP>的文本填充进去 xmlstarlet ed -L --inplace \ -u "/sec/NewTempP[following-sibling::*[1][self::fig]]/following-sibling::fig[1]/label" \ -x "../preceding-sibling::NewTempP[1]/text()" \ "$xmlfile"
脚本说明
/sec/NewTempP[following-sibling::*[1][self::fig]]:筛选出<sec>下所有**第一个后续兄弟是<fig>**的<NewTempP>元素/following-sibling::fig[1]/label:定位到该<NewTempP>紧邻的下一个<fig>中的<label>-x "../preceding-sibling::NewTempP[1]/text()":用XPath直接获取对应<NewTempP>的文本内容,作为<label>的新值,避免了内容提取和传递的错误
内容的提问来源于stack exchange,提问作者Ofuuzo
相关产品推荐
相关产品推荐

