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

如何使用Xmlstarlet正确向XML添加多个Resource元素?

如何用xmlstarlet给context.xml添加两个Resource元素

问题场景

需要向context.xml中添加两个Resource数据源配置,但现有脚本仅能正确添加一个,第二个元素配置失败,还出现「Attribute name redefined」错误。

原始context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
</Context>

期望输出:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Resource name="jdbc/DBRead" auth="Container"
        type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://127.0.0.1:5432/DBRead"
        username="abc" password="xyz" maxTotal="20" maxIdle="10"
        maxWaitMillis="-1"/>
  <Resource name="jdbc/DBWrite" auth="Container"
        type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://127.0.0.1:5432/DBWrite"
        username="abc" password="xyz" maxTotal="20" maxIdle="10"
        maxWaitMillis="-1"/>
</Context>

原脚本问题分析

原脚本每次单独执行xmlstarlet ed -L修改文件,存在两个核心问题:

  1. 当第一个Resource创建后,后续的//Context/Resource选择器会匹配所有已存在的Resource元素,导致给第一个元素重复添加属性(比如重复的name属性),触发「Attribute name redefined」错误。
  2. 创建第二个Resource时,命令中的-s '//Context/Resource'会给所有已存在的Resource加属性,而非仅给刚创建的第二个元素配置。

解决方案

使用位置选择器(//Context/Resource[1]、//Context/Resource[2])精确指定要操作的元素,同时尽量合并xmlstarlet命令减少文件IO,避免多次修改导致的匹配混乱。

方案1:合并为单个xmlstarlet命令(推荐)

#!/bin/sh
xmlstarlet ed -L \
  # 添加第一个Resource并配置所有属性
  -s '//Context' -t elem -n 'Resource' \
  -i '//Context/Resource[1]' -t attr -n 'name' -v 'jdbc/DBRead' \
  -i '//Context/Resource[1]' -t attr -n 'auth' -v 'Container' \
  -i '//Context/Resource[1]' -t attr -n 'type' -v 'javax.sql.DataSource' \
  -i '//Context/Resource[1]' -t attr -n 'driverClassName' -v 'org.postgresql.Driver' \
  -i '//Context/Resource[1]' -t attr -n 'url' -v 'jdbc:postgresql://127.0.0.1:5432/DBRead' \
  -i '//Context/Resource[1]' -t attr -n 'username' -v 'abc' \
  -i '//Context/Resource[1]' -t attr -n 'password' -v 'xyz' \
  -i '//Context/Resource[1]' -t attr -n 'maxTotal' -v '20' \
  -i '//Context/Resource[1]' -t attr -n 'maxIdle' -v '10' \
  -i '//Context/Resource[1]' -t attr -n 'maxWaitMillis' -v '-1' \
  # 添加第二个Resource并配置所有属性
  -s '//Context' -t elem -n 'Resource' \
  -i '//Context/Resource[2]' -t attr -n 'name' -v 'jdbc/DBWrite' \
  -i '//Context/Resource[2]' -t attr -n 'auth' -v 'Container' \
  -i '//Context/Resource[2]' -t attr -n 'type' -v 'javax.sql.DataSource' \
  -i '//Context/Resource[2]' -t attr -n 'driverClassName' -v 'org.postgresql.Driver' \
  -i '//Context/Resource[2]' -t attr -n 'url' -v 'jdbc:postgresql://127.0.0.1:5432/DBWrite' \
  -i '//Context/Resource[2]' -t attr -n 'username' -v 'abc' \
  -i '//Context/Resource[2]' -t attr -n 'password' -v 'xyz' \
  -i '//Context/Resource[2]' -t attr -n 'maxTotal' -v '20' \
  -i '//Context/Resource[2]' -t attr -n 'maxIdle' -v '10' \
  -i '//Context/Resource[2]' -t attr -n 'maxWaitMillis' -v '-1' \
  /home/context.xml

方案2:分步骤创建和配置(更易读)

先创建两个空的Resource元素,再分别给它们配置属性:

#!/bin/sh
# 第一步:添加两个空的Resource元素
xmlstarlet ed -L \
  -s '//Context' -t elem -n 'Resource' \
  -s '//Context' -t elem -n 'Resource' \
  /home/context.xml

# 第二步:配置第一个Resource
xmlstarlet ed -L \
  -i '//Context/Resource[1]' -t attr -n 'name' -v 'jdbc/DBRead' \
  -i '//Context/Resource[1]' -t attr -n 'auth' -v 'Container' \
  -i '//Context/Resource[1]' -t attr -n 'type' -v 'javax.sql.DataSource' \
  -i '//Context/Resource[1]' -t attr -n 'driverClassName' -v 'org.postgresql.Driver' \
  -i '//Context/Resource[1]' -t attr -n 'url' -v 'jdbc:postgresql://127.0.0.1:5432/DBRead' \
  -i '//Context/Resource[1]' -t attr -n 'username' -v 'abc' \
  -i '//Context/Resource[1]' -t attr -n 'password' -v 'xyz' \
  -i '//Context/Resource[1]' -t attr -n 'maxTotal' -v '20' \
  -i '//Context/Resource[1]' -t attr -n 'maxIdle' -v '10' \
  -i '//Context/Resource[1]' -t attr -n 'maxWaitMillis' -v '-1' \
  /home/context.xml

# 第三步:配置第二个Resource
xmlstarlet ed -L \
  -i '//Context/Resource[2]' -t attr -n 'name' -v 'jdbc/DBWrite' \
  -i '//Context/Resource[2]' -t attr -n 'auth' -v 'Container' \
  -i '//Context/Resource[2]' -t attr -n 'type' -v 'javax.sql.DataSource' \
  -i '//Context/Resource[2]' -t attr -n 'driverClassName' -v 'org.postgresql.Driver' \
  -i '//Context/Resource[2]' -t attr -n 'url' -v 'jdbc:postgresql://127.0.0.1:5432/DBWrite' \
  -i '//Context/Resource[2]' -t attr -n 'username' -v 'abc' \
  -i '//Context/Resource[2]' -t attr -n 'password' -v 'xyz' \
  -i '//Context/Resource[2]' -t attr -n 'maxTotal' -v '20' \
  -i '//Context/Resource[2]' -t attr -n 'maxIdle' -v '10' \
  -i '//Context/Resource[2]' -t attr -n 'maxWaitMillis' -v '-1' \
  /home/context.xml

关键说明

  • 使用//Context/Resource[N]格式的位置选择器,精准定位第N个Resource元素,避免误操作其他元素。
  • 合并命令能减少文件读写次数,提升执行效率;分步骤方式逻辑更清晰,适合后续维护调整。

内容的提问来源于stack exchange,提问作者Harshit Gupta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 05:05:37