Самая сложная задача, стоящая перед нами, состоит в создании 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