сравнить два файла xml и обновить определенный ключ только с помощью xmlstarlet

Я пытаюсь сравнить два файла 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>

Я нашел один из наиболее близких способов добиться этого в сообщении. Обновление двух файлов xml с помощью xmlstarlet, но все же требуется небольшое прикосновение. Так что, кажется, лучше использовать xmlstarlet, так как мне нужно запускать эту малышку в Debian изначально.

Было бы здорово хотя бы подсказать, как это использовать таким образом.

Заранее спасибо,

1 ответ

Вот два способа решить эту проблему, оба предполагают , что

  • элементы имеют уникальные nameс
  • используется оболочка POSIX

Во-первых, преобразование XSLT 1.0, которое добавит элемент из одноименного файла . Это базовое преобразование идентичности , которое изменяет соответствующие s. Если вы хотите ограничить определенные значения статуса, вы можете добавить, например, 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 сложнее, так как существуют ограничения как на xmlstarlet selectи операции: первый не копирует весь ввод, второй не принимает условные выражения (кроме выражений XPath). Однако, используя selectв качестве генератора кода для создания editкоманда возможна.(Чтобы вывести сгенерированный код XSLT 1.0, добавьте -Cвариант перед -t.)

      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

Для каждого -less triggerв этой команде ищет одноименный элемент в destination.xmlналичие непустого statusэлемент; при совпадении испускает -s(подузел) пункт для xmlstarlet editдля таргетинга на соответствующий узел в source.xml. -oвыводит буквальный текст, -nновая строка, -fвходной путь, -bзавершает текущий контейнер ( -m, --if, --var). Переменные sqа также dqпомочь в цитировании

Выход:

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

Беги как

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

для выполнения вывода в виде сценария оболочки.

Другие вопросы по тегам