如何使用 xmlstarlet 将元素插入 xml?

how to insert elements into xml with xmlstarlet?

我想在 XML 输入的一些变体中插入额外的元素。此脚本尝试演示输入,以及我尝试将代码插入现有的 XML。可以看出,修改 a.xml 给出了预期的输出。但在 b.xmlc.xml 中,结果是假的。在 b.xml 中,现有的 <c/> 被更改并创建了另一个 <b/> 的块。在c.xml中,结果是d=""被赋值了两次

应该只有一个<a><b>,有几个<c>

知道如何实现吗?

#!/bin/bash
# insert <a><b><c d="2"/>
set -e
td=`mktemp --directory --tmpdir=/dev/shm XXX`
trap "rm -rf '${td}'" EXIT
xmlstarlet --version
pushd "${td}"
cat > a.xml <<_EOX_
<a>
</a>
_EOX_
cat > b.xml <<_EOX_
<a>
 <b>
  <c/>
 </b>
</a>
_EOX_
cat > c.xml <<_EOX_
<a>
 <b>
  <c d="1"/>
 </b>
</a>
_EOX_

for i in *.xml
do
  echo "$i"
  cat "$i" | \
  xmlstarlet ed -O \
  -s 'a' -t elem -n b \
  -s 'a/b' -t elem -n c \
  -i 'a/b/c' -t attr -n d -v '2' |
  xmlstarlet fo -o || echo "$?"
done

这会产生以下输出:

1.6.1
compiled against libxml2 2.9.7, linked with 20907
compiled against libxslt 1.1.32, linked with 10132
/dev/shm/tpX ~/work
a.xml
<a>
  <b>
    <c d="2"/>
  </b>
</a>
37
b.xml
<a>
  <b>
    <c d="2"/>
    <c d="2"/>
  </b>
  <b>
    <c d="2"/>
  </b>
</a>
80
c.xml
-:3.19: Attribute d redefined
    <c d="1" d="2"/>
                  ^
2

问题是您需要告诉您要更改哪个节点。

  1. 仅当 ba 中不存在时添加 child b:

    -s "/a[not(b)]" -t elem -n "b"
    
  2. a/b中添加一个新的childc:

    -s "/a/b[not(c)]" -t elem -n "c"
    
  3. 将属性d添加到新添加的节点c,它在最后一个地方:

    -s "/a/b/c[last()]" -t attr -n "d" -v "2"
    

所以现在完整的命令是:

xmlstarlet ed -O                                        \ 
              -s "/a[not(b)]"     -t elem -n "b"        \
              -s "/a/b"           -t elem -n "c"        \
              -s "/a/b/c[last()]" -t attr -n "d" -v "2" 

这里我们使用 not() 函数来表示我们只想 select 不包含该特定项目的节点。

有用的链接: