Как найти узел XML и добавить или удалить его с помощью xmlstarlet - PullRequest
0 голосов
/ 30 мая 2018

Я пытаюсь создать сценарий оболочки, который ищет в файле XML атрибут и создать элемент с данным атрибутом, если он не существует, или удалить элемент, если атрибут существует.

Здесь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:

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>

Но мой скрипт работает так:

<?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>

Как изменить сценарий оболочки для достижения цели.Сначала я хочу добавить узел name = "fs102" на тот случай, если этот узел не существует.

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

Сценарий оболочки, который работает для переключения узла, выглядит следующим образом:

fs_name=fs
cnt=102
inputfile=distributor.conf.xml

if [ -n "$(xmlstarlet sel -T -t -v "//list[@name='CRproductionLoadshare']/node[@name='$fs_name$cnt']/@name" $inputfile)" ]; then
  echo "$fs_name$cnt already defined in $inputfile"
  xmlstarlet ed -L -d "//list[@name='CRproductionLoadshare']/node[@name='$fs_name$cnt']" $inputfile
else
  echo "adding $fs_name$cnt to $inputfile"
  xmlstarlet ed -L -s  "//list[@name='CRproductionLoadshare']" -t elem -n TempNode -v "" \
    -i //TempNode -t attr -n "name" -v "$fs_name$cnt" \
    -i //TempNode -t attr -n "weight" -v "2" \
    -r //TempNode -v node \
    $inputfile
fi

Каждый раз, когда я запускаю этот сценарий, файл ввода переключает узел (добавление / удаление) только в элементе списка с именем CRproductionLoadshare.

0 голосов
/ 06 июня 2018

Самая сложная задача, стоящая перед нами, состоит в создании XPath, который выбирает правильный узел.

Шаг 1: найдите нужный XPath

пример 1: Выберите узел с именем list, у которого есть атрибут @name="CRproductionLoadshare" и дочерний элемент с именем node с атрибутом @name="fs100".

Таким образом, вы можете искать родителя этого конкретного узла с именем 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" и не имеет дочернего элемента с именем node с атрибутом @name="fs102".

Здесь мы можем использовать XPath not -функцию

$ 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: отредактируйте ваш XML-файл с помощью только что найденного XPath

A: Просто добавьте узел, если его там нет

Итак, теперь вы знаете правильный XPath для выбора узлаВы можете соответствующим образом отредактировать XML-файл, сначала вставив подузел -s, а затем обновив его значения и атрибуты с помощью -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: переключить узел

Переключение можно сделать, добавив поддельный атрибут, а затем удалив узел с этим атрибутом:

$ 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
0 голосов
/ 30 мая 2018

поиск в XML-файле атрибута и его создание, если его не существует

fs_name="fs"
cnt=102

node_exists=$(xmlstarlet sel -t --var fs="'${fs_name}$cnt'" -v 'boolean(//list[@name="CRproductionLoadshare"]/node[@name=$fs])' distributor.conf.xml)
[ "$node_exists" = "false" ] && xmlstarlet ed -O -s '//list[@name="CRproductionLoadshare"]' \
-t elem -n node -i '//list[@name="CRproductionLoadshare"]/node[last()]' \
-t attr -n name -v "${fs_name}$cnt" \
-i '//list[@name="CRproductionLoadshare"]/node[last()]' -t attr -n weight -v 2 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>

Схема:

  • node_exists присваивается логическое значение, указывающее наличие необходимого узла
  • [ "$node_exists" = "false" ] && xmlstarlet ed ... - 2-я xmlstarlet команда редактирования будет выполняться только если node_exists не равно false
...