Как я могу использовать xmlstarlet для запроса тега на наличие двух значений атрибутов?

Вопрос или проблема

Пример:

<item href="cover.jpeg" id="cover" media-type="image/jpeg"/>

Мне нужно выбрать @id="cover" и media-type="image/*" и получить @href.

Мое текущее “решение” выглядит так…

xmlstarlet sel -t -m "//_:item[@id='$opf_cover_name']" -v @href -o '|' -v @media-type -n file.xml

…а затем следуют крайне неаккуратные операции по разбиению вывода вокруг символа ‘|’.

Есть ли у xmlstarlet функция подстроки?

$ xmlstarlet --version
Version = 1.6.1
compiled against libxml2 2.9.4, linked with 20904
compiled against libxslt 1.1.29, linked with 10132
the_id='cover'
the_mediatype_prefix='image/'

xmlstarlet sel -t \
    --var queryid="'$the_id'" \
    --var typeprefix="'$the_mediatype_prefix'" \
    -v '//item[@id = $queryid and starts-with(@media-type, $typeprefix)]/@href' \
    -nl file.xml

Этот запрос к XML-файлу использует две переменные оболочки. Переменная the_id содержит значение атрибута id, по которому мы фильтруем, а the_mediatype_prefix содержит строку, с которой должен начинаться атрибут media-type.

Мы создаем две внутренние переменные для xmlstarlet, используя опцию --var этого инструмента. Значения должны быть закодированными, поэтому я помещаю их в одинарные кавычки (это немного хитро, в идеале их следует правильно кодировать в строки XPath, но это немного лучше, чем напрямую вставлять переменные оболочки в выражение).

XPath выражение выбирает атрибут href каждого узла item, который имеет совпадающие id и media-type.


Используя xq (обертка для XML-парсера вокруг jq, парсера JSON) вместо этого:

the_id='cover'
the_mediatype_prefix='image/'

xq -r \
    --arg queryid "$the_id" \
    --arg typeprefix "$the_mediatype_prefix" '
    .. | .item? |
    select(
        ."@id" == $queryid and
        (."@media-type" | startswith($typeprefix))
    )."@href"' file.xml

Это более или менее идентично коду xmlstarlet, за исключением того, что используется выражение jq вместо запроса XPath. Здесь утилита заботится о правильном кодировании ваших переменных оболочки, даже если они содержат странные значения, такие как кавычки и т.д. (значения переменных оболочки в примере xmlstarlet пришлось бы кодировать вручную, если они содержат недопустимые последовательности символов).

Вы можете добавить несколько условий:

xmlstarlet sel -t -m "//item[@id='cover'][starts-with(@media-type,'image/')]" -v '@href' -nl file.xml

Ответ или решение

Для того чтобы использовать xmlstarlet для запроса тега с двумя атрибутами, вы можете воспользоваться выражением XPath, которое будет фильтровать элементы в зависимости от значений этих атрибутов. В вашем случае, вы хотите выбрать элемент <item>, используя атрибуты @id="cover" и @media-type со значением, начинающимся на "image/", и вернуть значение атрибута @href.

Вот как вы можете это сделать:

  1. Создайте две переменные в оболочке для хранения значений атрибутов, которые вы хотите использовать для фильтрации:
the_id='cover'
the_mediatype_prefix='image/'
  1. Используйте команду xmlstarlet sel с выражением XPath, чтобы выполнить выборку. В данном случае вы можете использовать следующие параметры:
xmlstarlet sel -t \
    --var queryid="'$the_id'" \
    --var typeprefix="'$the_mediatype_prefix'" \
    -v "//item[@id = $queryid and starts-with(@media-type, $typeprefix)]/@href" \
    -nl file.xml

Объяснение команд:

  • -t – указывает, что мы используем шаблон.
  • --var queryid="'$the_id'" – создается внутренняя переменная queryid, которая содержит значение cover. Одинарные кавычки необходимы для корректной обработки значения XPath.
  • --var typeprefix="'$the_mediatype_prefix'" – создается внутренняя переменная typeprefix, содержащая префикс "image/".
  • -v – указывает, что мы хотим вывести значение, соответствующее указанному пути. В данном случае это значение атрибута @href.
  • -nl – добавляет новую строку после вывода.

Этот запрос проанализирует файл file.xml и вернет значение атрибута @href, который соответствует условиям фильтрации.

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

xmlstarlet sel -t -m "//item[@id='cover'][starts-with(@media-type,'image/')]" -v '@href' -nl file.xml

Это выражение делает то же самое, не полагаясь на переменные оболочки.

Таким образом, вы можете легко извлекать необходимые значения атрибутов из XML, используя возможности xmlstarlet для работы с XPath-выражениями.

Оцените материал
Добавить комментарий

Капча загружается...