Вопрос или проблема
Пример:
<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
.
Вот как вы можете это сделать:
- Создайте две переменные в оболочке для хранения значений атрибутов, которые вы хотите использовать для фильтрации:
the_id='cover'
the_mediatype_prefix='image/'
- Используйте команду
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-выражениями.