简体   繁体   English

仅使用 xmlstarlet 比较两个 xml 文件并更新某些密钥

[英]compare two xml files for and update certain key only with xmlstarlet

I am trying to compare two of xml files and update only for a certain key as a new file.我正在尝试比较两个 xml 文件并仅将某个密钥更新为新文件。 The issue occurs when i export a zabbix template and try to import on the other environment, status should be left as destination one.当我导出 zabbix 模板并尝试在另一个环境中导入时,会出现此问题,状态应保留为目标之一。 Assume that i have two xml files,假设我有两个 xml 文件,

source.xml来源.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目的地.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> 

So my goal would be to create a new file and put the key/value "DISABLED" as following.所以我的目标是创建一个新文件并将键/值“禁用”如下所示。

final.xml最终的.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>

I've found one of the closest way to achieve this behave on the post Updating two xml file using xmlstarlet but still needs a small touch.我在使用 xmlstarlet 更新两个 xml 文件的帖子中找到了实现此行为的最接近的方法之一,但仍然需要一点点触摸。 So seems better to use 'xmlstarlet' since i need to run this babe in Debian natively.所以似乎更好地使用'xmlstarlet',因为我需要在 Debian 中本地运行这个宝贝。

It would be great at least give a clue how to use it in that way.至少提供一个线索如何以这种方式使用它会很棒。

Thanks in advance,提前致谢,

Here are two ways to solve it, both assume that这里有两种解决方法,都假设

  • trigger elements have unique name s trigger元素具有唯一的name
  • a POSIX shell is used使用 POSIX shell

First, an XSLT 1.0 transformation which will add a status element from a same-named trigger in destination.xml .首先,XSLT 1.0 转换将添加来自destination.xml中同名triggerstatus元素。 It's a basic identity transform which modifies the appropriate trigger s.这是一个基本的身份转换,它修改了适当的trigger In case you want to limit to certain status values you could add, for example, and $dstat = "DISABLED" to the xsl:if test clause.例如,如果您想限制某些状态值,可以将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>

Run as运行方式

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

or或者

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

Second, to do the same in shorthand is trickier since there are limitations on both the xmlstarlet select and xmlstarlet edit operations: the former doesn't copy entire input, the latter doesn't accept conditionals (except in XPath expressions).其次,在速记中做同样的事情比较棘手,因为xmlstarlet selectxmlstarlet edit操作都有限制:前者不复制整个输入,后者不接受条件(XPath 表达式除外)。 However, using select as a code generator to produce an edit command is possible.但是,使用select作为代码生成器来生成edit命令是可能的。 (To list the generated XSLT 1.0 code add -C option before -t .) (要列出生成的 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

For each status -less trigger in source.xml this command looks up the same-named element in destination.xml having a non-empty status element;对于source.xml中的每个无status trigger ,此命令在destination.xml中查找具有非空status元素的同名元素; on match emits an -s (subnode) clause for xmlstarlet edit to target the appropriate node in source.xml . on match 为xmlstarlet edit发出一个-s (子节点)子句,以针对source.xml中的适当节点。 -o outputs literal text, -n a newline, -f the input pathname, -b ends current container ( -m , --if , --var ). -o输出文字文本, -n换行符, -f输入路径名, -b结束当前容器( -m--if--var )。 Variables sq and dq assist in quoting变量sqdq有助于引用

Output: Output:

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

Run as运行方式

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

to execute the output as a shell script.执行 output 作为 shell 脚本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM