如何使用xmlstarlet在另一个元素下插入新元素?

sim*_*ico 25 xml xpath command-line command xmlstarlet

$ 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>
Run Code Online (Sandbox Code Playgroud)

但我希望sub成为配置的孩子.我该如何更改-ixpath参数

奖励:是否可以直接使用属性插入子项,甚至将其设置为值?就像是:

$ xmlstarlet ed -i "/config" -t elem -n "sub" -v ""  -a attr -n "class" -v "com.foo" test.xml
Run Code Online (Sandbox Code Playgroud)

小智 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>
Run Code Online (Sandbox Code Playgroud)

这是我想要实现的目标:

<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&amp;useUnicode=true&amp;characterEncoding=utf-8"
              username="DBUSER"
              password="DBPASS"
              maxActive="150"
              maxIdle="10"
              initialSize="10"
              validationQuery="SELECT 1"
              testOnBorrow="true" />
</GlobalNamingResources>
Run Code Online (Sandbox Code Playgroud)

我是这样做的(来自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
Run Code Online (Sandbox Code Playgroud)

诀窍是暂时为新元素指定一个唯一的名称,以便稍后可以使用XPATH表达式找到它.添加完所有属性后,名称将更改回Resource(使用-r).

其他xmlstarlet选项的含义:

-P (or --pf)        - preserve original formatting
-S (or --ps)        - preserve non-significant spaces
-L (or --inplace)   - edit file inplace
Run Code Online (Sandbox Code Playgroud)

  • 最后一个编辑操作(重命名)改变了游戏规则。对此感激不尽! (2认同)
  • +1 重命名,这是确保您只获得用于编辑的资源,然后在最后重命名的好方法,谢谢。 (2认同)

npo*_*avs 22

使用-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>
Run Code Online (Sandbox Code Playgroud)

  • 这种方法的问题在于,具有相同名称的任何其他XML元素也会获取该属性,这可能是不期望的.解决这个问题的方法是添加一个临时名称的元素,您可以将XPath唯一添加,并添加属性,然后在完成后保留元素. (4认同)

Ric*_*ker 7

从XMLStarlet(日期为2012年8月26日)的版本1.4.0,您可以使用$prev(或$xstar:prev)作为参数-i,-a以及-s指插入的最后节点集.看到XMLStarlet源代码示例中的文件doc/xmlstarlet.txt,examples/ed-backref1,examples/ed-backref2,和examples/ed-backref-delete.您不再需要使用插入具有临时元素名称的元素的技巧,然后在末尾重命名它.该示例examples/ed-backref2在显示如何定义用于引用先前创建的注释的变量时特别有用,这样您就不需要执行诸如$prev/.."导航"节点之类的技巧.