[英]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
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
中同名trigger
的status
元素。 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 xmlstarlet 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速记中做同样的事情比较棘手,因为xmlstarlet select
和xmlstarlet 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变量sq
和dq
有助于引用
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.