如何使用 xmlstarlet 搜索 XML 节点并添加或删除它 [英] How to search for a XML node and add or delete it using xmlstarlet

查看:35
本文介绍了如何使用 xmlstarlet 搜索 XML 节点并添加或删除它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试创建一个 shell 脚本,该脚本在 XML 文件中搜索一个属性,如果该属性不存在则创建一个具有给定属性的元素,如果该属性存在则删除该元素.

I try to create a shell script that searches in an XML file for an attribute and create an element with the given attribute if this doesn't exist or delete the element if the attribute exists.

这是 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:

And this is my Shellscript so far:

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>

但我的脚本是这样工作的:

But my script work like this:

<?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",以防该节点不存在.

How to change the shell script to reach the goal. At first, I want to add the node name="fs102" in case of that this node didn't exist.

推荐答案

这里最困难的任务是构建选择正确节点的 XPath.

The most difficult task at hand here is to build the XPath that selects the correct node.

示例 1: 选择名为 list 的节点,该节点具有属性 @name="CRproductionLoadshare" 并且有一个名为 的子节点节点 带有属性@name="fs100".

example 1: Select the node named list who has an attribute @name="CRproductionLoadshare" and has a child named node with attribute @name="fs100".

因此您可以搜索名为 node 的特定节点的父节点.

So you can search for the parent of that particular node named node.

$ 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: 选择名为 list 的节点,该节点具有属性 @name="CRproductionLoadshare"没有strong> 有一个名为 node 的子节点,其属性为 @name="fs102".

example 2: Select the node named list who has an attribute @name="CRproductionLoadshare" and does not have a child named node with attribute @name="fs102".

这里我们可以使用XPath not-function

Here we can use the 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 文件

A:如果节点不存在就添加节点

因此,由于您现在知道选择节点的正确 XPath,您可以通过首先插入子节点 -s 然后使用 更新其值和属性来相应地编辑 XML 文件-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:切换节点

可以通过添加一个假属性然后删除具有该属性的节点来完成切换:

B: Toggle the node

Toggling can be done by adding a fake attribute and then remove the node with that attribute:

$ 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

这篇关于如何使用 xmlstarlet 搜索 XML 节点并添加或删除它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆