$ vim test.xml
<?xml version="1.0" encoding="UTF-8" ?>
<config>
</config>
$ xmlstarlet ed -i "/config" -t elem -n "sub" -v "" test.xml
<?xml version="1.0" encoding="UTF-8"?>
<sub></sub>
<config>
</config>
但我希望sub成为config的孩子。我该如何更改xpath parameter of -i?
奖金: 是否可以直接使用属性插入子项,甚至将其设置为值? 类似的东西:
$ xmlstarlet ed -i "/config" -t elem -n "sub" -v "" -a attr -n "class" -v "com.foo" test.xml
答案 0 :(得分:34)
我遇到了类似的问题:我有一个Tomcat配置文件(server.xml),并且必须在<Resource>
部分插入带有预定义属性的<GlobalNamingResources>
标记。
以下是它的表现:
<GlobalNamingResources>
<!-- Editable user database that can also be used
by UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase"
auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
这是我想要实现的目标:
<GlobalNamingResources>
<!-- Editable user database that can also be used
by UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase"
auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
<Resource name="jdbc/templateassets"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://DBHOST:DBPORT/DBNAME?createDatabaseIfNotExist=false&useUnicode=true&characterEncoding=utf-8"
username="DBUSER"
password="DBPASS"
maxActive="150"
maxIdle="10"
initialSize="10"
validationQuery="SELECT 1"
testOnBorrow="true" />
</GlobalNamingResources>
我是这样做的(来自shell脚本的片段):
if [ -n "$(xmlstarlet sel -T -t -v "/Server/GlobalNamingResources/Resource[@name='jdbc/templateassets']/@name" server.xml)" ]; then
echo "Resource jdbc/templateassets already defined in server.xml"
else
echo "Adding resource jdbc/templateassets to <GlobalNamingResources> in server.xml"
xmlstarlet ed -P -S -L -s /Server/GlobalNamingResources -t elem -n ResourceTMP -v "" \
-i //ResourceTMP -t attr -n "name" -v "jdbc/templateassets" \
-i //ResourceTMP -t attr -n "auth" -v "Container" \
-i //ResourceTMP -t attr -n "type" -v "javax.sql.DataSource" \
-i //ResourceTMP -t attr -n "driverClassName" -v "com.mysql.jdbc.Driver" \
-i //ResourceTMP -t attr -n "url" -v "jdbc:mysql://DBHOST:DBPORT/DBNAME?createDatabaseIfNotExist=false&useUnicode=true&characterEncoding=utf-8" \
-i //ResourceTMP -t attr -n "username" -v "DBUSER" \
-i //ResourceTMP -t attr -n "password" -v "DBPASS" \
-i //ResourceTMP -t attr -n "maxActive" -v "150" \
-i //ResourceTMP -t attr -n "maxIdle" -v "10" \
-i //ResourceTMP -t attr -n "initialSize" -v "10" \
-i //ResourceTMP -t attr -n "validationQuery" -v "SELECT 1" \
-i //ResourceTMP -t attr -n "testOnBorrow" -v "true" \
-r //ResourceTMP -v Resource \
server.xml
fi
诀窍是暂时为新元素指定一个唯一的名称,以便稍后可以使用XPATH表达式找到它。添加完所有属性后,名称将更改回资源(使用-r)。
其他xmlstarlet选项的含义:
-P (or --pf) - preserve original formatting
-S (or --ps) - preserve non-significant spaces
-L (or --inplace) - edit file inplace
答案 1 :(得分:19)
使用-s
(或--subnode
)代替-i
。关于奖金,您不能直接插入具有属性的元素,但由于每个编辑操作都是按顺序执行,因此要插入元素然后添加属性:
> xml ed -s /config -t elem -n sub -v "" -i /config/sub -t attr -n class -v com.foo test.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<sub class="com.foo"></sub></config>
答案 2 :(得分:6)
从XMLStarlet版本1.4.0(2012-08-26)开始,您可以使用$prev
(或$xstar:prev
)作为-i
的参数,-a
,和-s
引用插入的最后一个节点集。请参阅文件doc/xmlstarlet.txt
,examples/ed-backref1
,examples/ed-backref2
和examples/ed-backref-delete
中的XMLStarlet源代码中的示例。您不再需要使用插入具有临时元素名称的元素的技巧,然后在末尾重命名它。示例examples/ed-backref2
特别有助于展示如何定义用于引用(之前)创建的注释的变量,这样您就不需要执行诸如$prev/..
之类的操作来导航“从一个节点出来。
答案 3 :(得分:1)
该示例在将<GlobalNamingResources>
包装到<Server>
元素中后才起作用。
答案 4 :(得分:0)
我尝试了上面cellux的技巧,效果很好!谢谢!! 但是,格式化并没有持久化,只是尝试一下,我摆脱了-P和-S选项,格式化问题消失了!我正在使用CentOS。也许这可以帮助某人。
答案 5 :(得分:0)
正如@npoostavs 所提到的,正确的答案是使用“子节点”。要使用新的“$prev”改进答案,您可以执行以下操作:
xml ed --inplace \
--subnode /config --type elem --name "sub" \
--var new_node '$prev' \
--insert '$new_node' --type attr --name "class" --value "com.foo" \
test.xml
以下说明:
--inplace Change the file "test.xml" directly
--subnode Add a new node called "class" below "/config"
--var Assign the newly created node to the variable new_node
Use single quotes to prevent bash replacing the variable
--insert Insert attribute and value to the newly created node