【问题标题】:compare two xml files for and update certain key only with xmlstarlet比较两个 xml 文件并仅使用 xmlstarlet 更新某些密钥
【发布时间】:2021-12-24 23:01:36
【问题描述】:

我正在尝试比较两个 xml 文件并仅将某个键更新为新文件。当我导出 zabbix 模板并尝试在另一个环境中导入时会出现此问题,状态应保留为目标之一。假设我有两个 xml 文件,

source.xml

<zabbix_export>
    <version>5.0</version>
    <groups>
        <group>
            <name>zabbix</name>
        </group>
    </groups>
    <templates>
        <template>
            <template>testtemp</template>
            <name>testtemp</name>
            <groups>
                <group>
                    <name>zabbix</name>
                </group>
            </groups>
            <items>
                <item>
                    <name>test1</name>
                    <key>kernel.maxproc</key>
                    <triggers>
                        <trigger>
                            <expression>{last()}=0</expression>
                            <name>testtrig1</name>
                        </trigger>
                        <trigger>
                            <expression>{last()}=100</expression>
                            <name>testtrig2</name>
                        </trigger>
                    </triggers>
                </item>
            </items>
        </template>
    </templates>
</zabbix_export>

destination.xml

    <version>5.0</version>
    <groups>
        <group>
            <name> zabbix </name>
        </group>
    </groups>
    <templates>
        <template>
            <template>testtemp</template>
            <name>testtemp</name>
            <groups>
                <group>
                    <name>zabbix</name>
                </group>
            </groups>
            <items>
                <item>
                    <name>test1</name>
                    <key>kernel.maxproc</key>
                    <triggers>
                        <trigger>
                            <expression>{last()}=0</expression>
                            <name>testtrig1</name>
                            <status>DISABLED</status>
                        </trigger>
                    </triggers>
                </item>
            </items>
        </template>
    </templates>
</zabbix_export> 

所以我的目标是创建一个新文件并将键/值“禁用”如下。

final.xml

<zabbix_export>
    <version>5.0</version>
    <groups>
        <group>
            <name>zabbix</name>
        </group>
    </groups>
    <templates>
        <template>
            <template>testtemp</template>
            <name>testtemp</name>
            <groups>
                <group>
                    <name>zabbix</name>
                </group>
            </groups>
            <items>
                <item>
                    <name>test1</name>
                    <key>kernel.maxproc</key>
                    <triggers>
                        <trigger>
                            <expression>{last()}=0</expression>
                            <name>testtrig1</name>
                            <status>DISABLED</status>
                        </trigger>
                        <trigger>
                            <expression>{last()}=100</expression>
                            <name>testtrig2</name>
                        </trigger>
                    </triggers>
                </item>
            </items>
        </template>
    </templates>
</zabbix_export>

我在Updating two xml file using xmlstarlet 帖子上找到了实现此行为的最接近的方法之一,但仍需要稍作改动。所以使用 'xmlstarlet' 似乎更好,因为我需要在 Debian 本地运行这个宝贝。

至少能提供一个线索如何以这种方式使用它会很棒。

提前致谢,

【问题讨论】:

  • destination.xml 不是 XML,除非您在文件开头插入 &lt;zabbix_export&gt;。在final.xml //group[1]/name 来自source.xml//trigger[1] 来自destination.xml;解释?你尝试了什么xmlstarlet 命令?
  • 两个xml之间的唯一区别是&lt;status&gt;DISABLED&lt;/status&gt;标签,所以我只想将此标签插入final.xml,如果目的地有它。
  • 如果destination和final之间的唯一区别是final有&lt;status&gt;DISABLED&lt;/status&gt;,为什么不检查destination是否有标签,如果有,就使用destination作为final(即复制目的地并将其称为“最终”)?
  • 视情况而定。问题是我必须为某些上键插入&lt;status&gt;DISABLED&lt;/status&gt;,在这种情况下,对于名为&lt;name&gt;testtrig1&lt;/name&gt;但所有其他&lt;trigger&gt;的触发器。所以也需要弄清楚delta。
  • 基本上,如果触发器在目标上但在源中被禁用,则 final.xml 也应为所涉及的触发器禁用。

标签: xml zabbix xmlstarlet xmlupdate


【解决方案1】:

这里有两种解决方法,都假设

  • trigger 元素具有唯一的names
  • 使用了 POSIX shell

首先,一个 XSLT 1.0 转换将添加一个 status 元素 来自destination.xml 中的同名trigger。这是一个基本的 identity transform 它修改了相应的triggers。如果你想限制 您可以添加到某些状态值,例如, and $dstat = "DISABLED"xsl:if test 子句。

<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:strip-space elements="*"/>
  <xsl:param name="ddoc" select="'destination.xml'"/>
  <xsl:variable name="dtt" select="document($ddoc)//triggers/trigger"/>
  
  <xsl:template match="@*|node()" name="identity">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="triggers/trigger[not(status)]">
    <xsl:variable name="dstat" 
        select="$dtt[name = current()/name]/status"/>
    <xsl:copy>
      <xsl:apply-templates/>
      <xsl:if test="string($dstat)">
        <xsl:copy-of select="$dstat"/>
      </xsl:if>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

运行方式

xsltproc --stringparam ddoc destination.xml delta.xsl source.xml > final.xml

xmlstarlet tr delta.xsl -s ddoc=destination.xml source.xml > final.xml

其次,在 速记中做同样的事情比较棘手,因为 xmlstarlet selectxmlstarlet edit 都有限制 操作:前者不复制整个输入,后者不复制 接受条件(XPath 表达式除外)。但是,使用 select 作为代码生成器生成edit 命令是可能的。 (要列出生成的 XSLT 1.0 代码,请在 -t 之前添加 -C 选项。)

xmlstarlet sel -t \
  --var sq -o "'" -b \
  --var dq -o '"' -b \
  --var ddoc="'destination.xml'" \
  --var dtt='document($ddoc)//triggers/trigger' \
  -o 'xmlstarlet edit \' -n \
  -m '//triggers/trigger[not(status)]' \
    --var dstat='$dtt[name = current()/name]/status' \
    --if 'string($dstat)' \
      -o ' -s ' -v 'concat($sq,"//triggers/trigger[name=",$dq,current()/name,$dq,"]",$sq)' \
      -o ' -t elem -n status -v ' -v 'concat($sq,$dstat,$sq)' -o ' \' -n \
    -b \
  -b -f -n source.xml

对于source.xml 中的每个status-less trigger,此命令查找 destination.xml 中的同名元素具有非空 status 元素; on match 为xmlstarlet edit 发出一个-s(子节点)子句 以source.xml 中的适当节点为目标。 -o 输出文字 文本,-n 换行符,-f 输入路径名,-b 结束当前 容器(-m--if--var)。变量sqdq 协助 引用

输出:

xmlstarlet edit \
 -s '//triggers/trigger[name="testtrig1"]' -t elem -n status -v 'DISABLED' \
source.xml

运行方式

xmlstarlet-sel-command-above | sh -s > final.xml

将输出作为 shell 脚本执行。

【讨论】:

    猜你喜欢
    • 2018-08-02
    • 1970-01-01
    • 2023-03-23
    • 2022-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多