← Весь справочник

xmlstarlet

Запросы XPath к XML, правка элементов и атрибутов, проверка и форматирование — всё из командной строки

xmlstarlet — набор утилит для XML в одной команде: sel достаёт данные по XPath, ed правит документ, val проверяет корректность и схему, fo форматирует. Для XML это то же, что jq для JSON.

Синтаксис

xmlstarlet sel [опции] -t шаблон [файл…]
xmlstarlet ed  [опции] действие… [файл…]
xmlstarlet val [опции] файл…
xmlstarlet fo  [опции] [файл]

Без файла (или с -) читает stdin.

Подкоманды

КомандаЧто делает
sel (select)выбрать данные по XPath, вывести текст или XML
ed (edit)изменить, добавить, удалить, переименовать, переместить узлы
val (validate)проверить, что XML корректен; по DTD, XSD или RelaxNG
fo (format)отформатировать с отступами
el (elements)показать структуру: пути всех элементов
tr (transform)применить XSLT
esc / unescэкранировать / разэкранировать <, >, &

sel: выборка

Глобальные опции идут до -t, шаблон — после.

Глобальная опцияЧто делает
-Tвыводить как текст: без него -o '<a&b>' напечатает &lt;a&amp;b&gt;
-N p=URIобъявить префикс пространства имён
-Qничего не печатать, только код выхода
-Iотступы в XML-выводе
-Bубрать незначащие пробелы
Опция шаблонаЧто делает
-tначать шаблон (их может быть несколько)
-v xpathвывести значение (текст)
-c xpathвывести копию узла с тегами
-m xpathдля каждого найденного узла выполнить следующие действия (цикл)
-o 'строка'вывести строку как есть
-nперевод строки
-i xpath / --elif / --elseусловие
-s A:N:- xpathсортировка внутри -m: A/D — по возрастанию/убыванию, N/T — как числа/текст
-e имя / -a имясоздать элемент / атрибут в выводе
-bзакончить текущий -m или -i
-fвывести имя входного файла
--var имя=xpathпеременная $имя

ed: правка

ДействиеЧто делает
-u xpath -v значениезаменить текст узла или значение атрибута
-u xpath -x выражениезаменить на результат XPath: -x '. * 2'
-d xpathудалить узлы
-s xpath -t elem|attr|text -n имя -v значениедобавить дочерний узел (subnode) — в конец
-i xpath -t … -n имя -v …вставить перед узлом; с -t attr — добавить атрибут этому узлу
-a xpath -t … -n имя -v …вставить после узла
-r xpath -v новое_имяпереименовать элемент или атрибут
-m xpath откуда xpath кудапереместить узел
Опция edЧто делает
-Lправить файл на месте
-Pсохранить пробелы и переносы как были
-Oне печатать <?xml …?>
-N p=URIпрефикс пространства имён (ставится последней из глобальных)

val, fo, el

Команда и опцияЧто делает
val файл…только корректность (well-formed): файл - valid / invalid
val -eпоказать, что не так (строка, столбец, причина)
val -s схема.xsd / -d файл.dtd / -r схема.rngпроверка по схеме
val -qничего не печатать, только код выхода
val -b / -gвывести только плохие / только хорошие файлы
foотступ в 2 пробела; -s N — N пробелов, -t — табы, -o — без <?xml?>, -R — попытаться починить битый XML
elпути элементов; -a — с атрибутами, -v — с их значениями, -u — без повторов

Коды выхода

sel — 1, если вывод пустой, иначе 0. Код зависит именно от вывода: -v '//nope' -n напечатает перевод строки и вернёт 0. Для проверки «есть ли узел» бери -m: sel -t -m '//nope' -v . -n вернёт 1, если узлов нет. val — 0, если все файлы прошли проверку, иначе 1.

Примеры

cat > books.xml <<'EOF'
<catalog>
  <book id="b1" lang="en"><title>Linux Basics</title><price>10.5</price></book>
  <book id="b2" lang="ru"><title>Bash</title><price>20</price></book>
  <book id="b3" lang="en"><title>Networks</title><price>15</price></book>
</catalog>
EOF

# все заголовки, по одному в строке
xmlstarlet sel -t -v '//title' -n books.xml

# значение атрибута
xmlstarlet sel -t -v '//book[2]/@id' -n books.xml

# таблица: id и название для каждой книги
xmlstarlet sel -T -t -m '//book' -v '@id' -o $'\t' -v 'title' -n books.xml

# фильтр по атрибуту и по числу
xmlstarlet sel -t -m '//book[@lang="en"]' -v title -n books.xml
xmlstarlet sel -t -m '//book[price > 12]' -v title -n books.xml

# функции XPath: количество, сумма
xmlstarlet sel -t -v 'count(//book)' -n books.xml
xmlstarlet sel -t -v 'sum(//price)' -n books.xml

# сортировка по цене по убыванию
xmlstarlet sel -T -t -m '//book' -s D:N:- price -v 'concat(@id,",",price)' -n books.xml

# изменить цену конкретной книги — в stdout
xmlstarlet ed -u '//book[@id="b1"]/price' -v 12 books.xml

# то же, но в самом файле
xmlstarlet ed -L -u '//book[@id="b1"]/price' -v 12 books.xml

# добавить атрибут, дочерний элемент, удалить узел
xmlstarlet ed -i '//book[1]' -t attr -n year -v 2024 books.xml
xmlstarlet ed -s '//book[1]' -t elem -n author -v 'A. Author' books.xml
xmlstarlet ed -d '//book[@lang="ru"]' books.xml

# пересчитать все цены выражением
xmlstarlet ed -u '//price' -x '. * 2' books.xml

# проверить корректность, с подробной ошибкой
xmlstarlet val -e books.xml

# проверить по XSD
xmlstarlet val -e -s schema.xsd books.xml

# отформатировать однострочный XML
echo '<a><b>1</b><c/></a>' | xmlstarlet fo

# структура незнакомого документа
xmlstarlet el -u books.xml

Подводные камни

  • Пространство имён по умолчанию. Если корень объявляет xmlns="…", то //item ничего не найдёт: элемент на самом деле в пространстве имён. Объяви префикс -N x=URI и пиши //x:item, или используй встроенный префикс _: //_:item.
  • Без -n последняя строка без перевода строки — в терминале приглашение «прилипнет» к выводу.
  • -v с набором узлов печатает все значения (через перевод строки), а не только первое, как в XSLT 1.0.
  • ed переформатирует документ. Однострочные элементы разъезжаются по строкам с отступами. Нужен исходный вид — добавляй -P, причём до действий: ed -P -u …, а не в конце.
  • ed без -L файл не меняет, только печатает результат. А -L молча перезаписывает файл — сначала проверь вывод без него.
  • val по умолчанию не говорит, что не так — только invalid. Добавляй -e.
  • val без схемы проверяет только синтаксис. Документ с неверными значениями будет valid, пока не передан -s, -d или -r.
  • Кавычки. XPath бери в одинарные кавычки оболочки, а строки внутри — в двойные: '//book[@lang="en"]'.
  • sel возвращает 1 на пустой вывод. В скриптах с set -e это прервёт выполнение; а -n в шаблоне, наоборот, маскирует «ничего не найдено» под успех.

См. также

xmllint · jq · yq · iconv