如何使用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修改文件,存在两个核心问题:
- 当第一个
Resource创建后,后续的//Context/Resource选择器会匹配所有已存在的Resource元素,导致给第一个元素重复添加属性(比如重复的name属性),触发「Attribute name redefined」错误。 - 创建第二个
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
相关产品推荐
相关产品推荐

