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>' напечатает <a&b> |
-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