You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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>

问题原因

  1. 上下文丢失:xmlstarlet sel -c "/sec/NewTempP"输出的是独立的<NewTempP>元素片段,没有包含它的兄弟节点。后续用following-sibling::fig/label查询时,因为当前XML片段只有<NewTempP>,根本找不到对应的<fig>元素。
  2. XPath作用域错误:就算上下文正确,xmlstarlet ed的-u参数中的XPath是基于整个文档的,不是当前循环的<NewTempP>,会错误地修改所有符合条件的<label>,而非对应那个<NewTempP>的后续节点。
  3. 参数格式错误:- 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:34:56