如何使用 xmlstarlet 搜索 XML 节点并添加或删除它
How to search for a XML node and add or delete it using xmlstarlet
我尝试创建一个 shell 脚本,该脚本在 XML 文件中搜索属性,如果不存在则创建具有给定属性的元素,如果属性不存在则删除该元素存在。
这是 XML 文件:
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
到目前为止,这是我的 Shellscript:
fs_name=fs
cnt=102
xmlstarlet ed \
--var fs "'$fs_name$cnt'" \
-a '//list' -t elem -n node -v "$fs_name$cnt" \
-i '//node' -t attr -n name -v "$fs_name$cnt" \
-i '//node' -t attr -n weight -v 2 \
-d '//node[.=$fs]/text()' <distributor.conf.xml
预期输出是
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
<node name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
但我的脚本是这样工作的:
<?xml version="1.0"?>
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2" name="fs102" weight="2"/>
<node name="fs101" weight="2" name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2" name="fs102" weight="2"/>
</list>
<node name="fs102" weight="2"/>
</lists>
</configuration>
如何更改 shell 脚本以达到目标。首先,我想添加节点名称="fs102" 以防该节点不存在。
search in a XML file for a attribute and create it if this doesn't
exist
fs_name="fs"
cnt=102
node_exists=$(xmlstarlet sel -t --var fs="'${fs_name}$cnt'" -v 'boolean(//list[@name="CRproductionLoadshare"]/node[@name=$fs])' distributor.conf.xml)
[ "$node_exists" = "false" ] && xmlstarlet ed -O -s '//list[@name="CRproductionLoadshare"]' \
-t elem -n node -i '//list[@name="CRproductionLoadshare"]/node[last()]' \
-t attr -n name -v "${fs_name}$cnt" \
-i '//list[@name="CRproductionLoadshare"]/node[last()]' -t attr -n weight -v 2 distributor.conf.xml
输出:
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
<node name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
方案:
node_exists
被赋值布尔值表示需要的节点存在
[ "$node_exists" = "false" ] && xmlstarlet ed ...
- 第二个 xmlstarlet
edit 命令只会在 node_exists
不等于 false
时执行
用于切换节点的 shell 脚本如下所示:
fs_name=fs
cnt=102
inputfile=distributor.conf.xml
if [ -n "$(xmlstarlet sel -T -t -v "//list[@name='CRproductionLoadshare']/node[@name='$fs_name$cnt']/@name" $inputfile)" ]; then
echo "$fs_name$cnt already defined in $inputfile"
xmlstarlet ed -L -d "//list[@name='CRproductionLoadshare']/node[@name='$fs_name$cnt']" $inputfile
else
echo "adding $fs_name$cnt to $inputfile"
xmlstarlet ed -L -s "//list[@name='CRproductionLoadshare']" -t elem -n TempNode -v "" \
-i //TempNode -t attr -n "name" -v "$fs_name$cnt" \
-i //TempNode -t attr -n "weight" -v "2" \
-r //TempNode -v node \
$inputfile
fi
每次我 运行 编写输入文件脚本时,仅在名称为 CRproductionLoadshare 的列表元素中切换节点 (add/delete)。
手头最困难的任务是构建 select 正确节点的 XPath。
第 1 步:找到您需要的 XPath
示例 1: Select 名为 list
的节点具有属性 @name="CRproductionLoadshare"
并且具有名为 child 的节点 node
具有属性 @name="fs100"
.
因此您可以搜索名为 node
的特定节点的 parent。
$ xmlstarlet sel -t \
-m '//node[@name="fs100"]/parent::list[@name="CRproductionLoadshare"]' \
-c . -n foo.xml
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
</list>
或者更简单一点:
$ xmlstarlet sel -t \
-m '//list[@name="CRproductionLoadshare" and node[@name="fs100"]]' \
-c . -n foo.xml
示例 2: Select 名为 list
的节点具有属性 @name="CRproductionLoadshare"
而 没有 有一个名为 node
的 child 属性 @name="fs102"
.
这里我们可以使用XPath not
-function
$ xmlstarlet sel -t \
-m '//list[@name="CRproductionLoadshare" and not(node[@name="fs102"])]' \
-c . -n foo.xml
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
</list>
第 2 步:使用您刚刚找到的 XPath 编辑您的 XML-file
答:没有节点就添加
因此,由于您现在知道 select 节点的正确 XPath,因此您可以通过首先插入子节点 -s
然后更新其值和属性来相应地编辑 XML-file -i
$ xpath1='//list[@name="CRproductionLoadshare" and not(node[@name="fs102"])]'
$ xpath2='//list[@name="CRproductionLoadshare" and not(node[@name="fs102" and @weight="2"])]/node[last()]'
$ xmlstarlet ed -s ${xpath1} -t elem -n "node" -v "" \
-i ${xpath2} -t attr -n "name" -v "fs102" \
-i ${xpath2} -t attr -n "weight" -v "2" \
foo.xml
输出
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
<node name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
B:切换节点
可以通过添加假属性然后删除具有该属性的节点来完成切换:
$ xpath0='//list[@name="CRproductionLoadshare"]/node[@name="fs102"]'
$ xpath1='//list[@name="CRproductionLoadshare" and not(node[@name="fs102" and @delete="1"])]'
$ xpath2='//list[@name="CRproductionLoadshare" and not(node[@name="fs102" and @delete="1"])]/node[last()]'
$ xpath3='//list[@name="CRproductionLoadshare"]/node[@name="fs102" and @delete="1"]'
$ xmlstarlet ed -i ${xpath0} -t attr -n "delete" -v "1" \
-s ${xpath1} -t elem -n "node" -v "" \
-i ${xpath2} -t attr -n "name" -v "fs102" \
-i ${xpath2} -t attr -n "weight" -v "2" \
-d ${xpath3} \
foo.xml
我尝试创建一个 shell 脚本,该脚本在 XML 文件中搜索属性,如果不存在则创建具有给定属性的元素,如果属性不存在则删除该元素存在。
这是 XML 文件:
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
到目前为止,这是我的 Shellscript:
fs_name=fs
cnt=102
xmlstarlet ed \
--var fs "'$fs_name$cnt'" \
-a '//list' -t elem -n node -v "$fs_name$cnt" \
-i '//node' -t attr -n name -v "$fs_name$cnt" \
-i '//node' -t attr -n weight -v 2 \
-d '//node[.=$fs]/text()' <distributor.conf.xml
预期输出是
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
<node name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
但我的脚本是这样工作的:
<?xml version="1.0"?>
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2" name="fs102" weight="2"/>
<node name="fs101" weight="2" name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2" name="fs102" weight="2"/>
</list>
<node name="fs102" weight="2"/>
</lists>
</configuration>
如何更改 shell 脚本以达到目标。首先,我想添加节点名称="fs102" 以防该节点不存在。
search in a XML file for a attribute and create it if this doesn't exist
fs_name="fs"
cnt=102
node_exists=$(xmlstarlet sel -t --var fs="'${fs_name}$cnt'" -v 'boolean(//list[@name="CRproductionLoadshare"]/node[@name=$fs])' distributor.conf.xml)
[ "$node_exists" = "false" ] && xmlstarlet ed -O -s '//list[@name="CRproductionLoadshare"]' \
-t elem -n node -i '//list[@name="CRproductionLoadshare"]/node[last()]' \
-t attr -n name -v "${fs_name}$cnt" \
-i '//list[@name="CRproductionLoadshare"]/node[last()]' -t attr -n weight -v 2 distributor.conf.xml
输出:
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
<node name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
方案:
node_exists
被赋值布尔值表示需要的节点存在[ "$node_exists" = "false" ] && xmlstarlet ed ...
- 第二个xmlstarlet
edit 命令只会在node_exists
不等于false
时执行
用于切换节点的 shell 脚本如下所示:
fs_name=fs
cnt=102
inputfile=distributor.conf.xml
if [ -n "$(xmlstarlet sel -T -t -v "//list[@name='CRproductionLoadshare']/node[@name='$fs_name$cnt']/@name" $inputfile)" ]; then
echo "$fs_name$cnt already defined in $inputfile"
xmlstarlet ed -L -d "//list[@name='CRproductionLoadshare']/node[@name='$fs_name$cnt']" $inputfile
else
echo "adding $fs_name$cnt to $inputfile"
xmlstarlet ed -L -s "//list[@name='CRproductionLoadshare']" -t elem -n TempNode -v "" \
-i //TempNode -t attr -n "name" -v "$fs_name$cnt" \
-i //TempNode -t attr -n "weight" -v "2" \
-r //TempNode -v node \
$inputfile
fi
每次我 运行 编写输入文件脚本时,仅在名称为 CRproductionLoadshare 的列表元素中切换节点 (add/delete)。
手头最困难的任务是构建 select 正确节点的 XPath。
第 1 步:找到您需要的 XPath
示例 1: Select 名为 list
的节点具有属性 @name="CRproductionLoadshare"
并且具有名为 child 的节点 node
具有属性 @name="fs100"
.
因此您可以搜索名为 node
的特定节点的 parent。
$ xmlstarlet sel -t \
-m '//node[@name="fs100"]/parent::list[@name="CRproductionLoadshare"]' \
-c . -n foo.xml
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
</list>
或者更简单一点:
$ xmlstarlet sel -t \
-m '//list[@name="CRproductionLoadshare" and node[@name="fs100"]]' \
-c . -n foo.xml
示例 2: Select 名为 list
的节点具有属性 @name="CRproductionLoadshare"
而 没有 有一个名为 node
的 child 属性 @name="fs102"
.
这里我们可以使用XPath not
-function
$ xmlstarlet sel -t \
-m '//list[@name="CRproductionLoadshare" and not(node[@name="fs102"])]' \
-c . -n foo.xml
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
</list>
第 2 步:使用您刚刚找到的 XPath 编辑您的 XML-file
答:没有节点就添加
因此,由于您现在知道 select 节点的正确 XPath,因此您可以通过首先插入子节点 -s
然后更新其值和属性来相应地编辑 XML-file -i
$ xpath1='//list[@name="CRproductionLoadshare" and not(node[@name="fs102"])]'
$ xpath2='//list[@name="CRproductionLoadshare" and not(node[@name="fs102" and @weight="2"])]/node[last()]'
$ xmlstarlet ed -s ${xpath1} -t elem -n "node" -v "" \
-i ${xpath2} -t attr -n "name" -v "fs102" \
-i ${xpath2} -t attr -n "weight" -v "2" \
foo.xml
输出
<configuration name="distributor.conf" description="Distributor Configuration">
<lists>
<list name="CRproductionLoadshare">
<node name="fs100" weight="2"/>
<node name="fs101" weight="2"/>
<node name="fs102" weight="2"/>
</list>
<list name="AnyOtherGroup">
<node name="fs100" weight="2"/>
</list>
</lists>
</configuration>
B:切换节点
可以通过添加假属性然后删除具有该属性的节点来完成切换:
$ xpath0='//list[@name="CRproductionLoadshare"]/node[@name="fs102"]'
$ xpath1='//list[@name="CRproductionLoadshare" and not(node[@name="fs102" and @delete="1"])]'
$ xpath2='//list[@name="CRproductionLoadshare" and not(node[@name="fs102" and @delete="1"])]/node[last()]'
$ xpath3='//list[@name="CRproductionLoadshare"]/node[@name="fs102" and @delete="1"]'
$ xmlstarlet ed -i ${xpath0} -t attr -n "delete" -v "1" \
-s ${xpath1} -t elem -n "node" -v "" \
-i ${xpath2} -t attr -n "name" -v "fs102" \
-i ${xpath2} -t attr -n "weight" -v "2" \
-d ${xpath3} \
foo.xml