Почему xmlstarlet говорит, что нет функции "заканчивается"?
Я использую xmlstarlet для извлечения changeSet
узлы из журнала изменений XML ликвидности, где viewName
заканчивается буквой "v".
Тем не менее, xmlstarlet жалуется, что ends-with
Функция XPATH не существует:
$ xmlstarlet sel -N x="http://www.liquibase.org/xml/ns/dbchangelog" -t -m \
"/x:databaseChangeLog/x:changeSet[x:createView[ends-with(@viewName, 'v')]]" \
-c . public.db.changelog.xml
xmlXPathCompOpEval: function ends-with not found
Unregistered function
Stack usage errror
xmlXPathCompiledEval: 3 objects left on the stack.
runtime error: element for-each
Failed to evaluate the 'select' expression.
None of the XPaths matched; to match a node in the default namespace
use '_' as the prefix (see section 5.1 in the manual).
For instance, use /_:node instead of /node
XML выглядит примерно так:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="1391529990457-3">
<createView viewName="myviewnamev"><!-- view definition here --></createView>
</changeSet>
<changeSet id="1391529990457-4">
<createView viewName="anotherviewname"><!-- view definition here --></createView>
</changeSet>
</databaseChangeLog>
Я знаю, что выражение XPATH в противном случае правильно, потому что если я изменю критерии выбора на x:createView[@viewName="myviewnamev"]
тогда он правильно выбирает только то, что changeLog
запись.
Как мне заставить xmlstarlet правильно использовать ends-with
? Или есть альтернативный способ выполнить то, что я хочу сделать?
3 ответа
xmlstarlet
поддерживает только XPath 1.0, который не предлагает ends-with($string, $token)
функция. Вам нужно использовать substring
, string-length
и и сравнение строк, чтобы создать свой собственный, используя этот шаблон:
substring($string, string-length($string) - string-length($token) + 1) = $token]
Применительно к вашему запросу он должен выглядеть следующим образом (я "предварительно вычисляю" длину строки):
/x:databaseChangeLog/x:changeSet[x:createView[
substring(@viewName, string-length(@viewName)) = 'v']
]
В качестве альтернативы вы можете поискать более мощный движок XPath 2.0/XQuery.
$ xml sel -t -c //_:changeSet[_:createView[str:split(@viewName,'')[last()]='v']] -n file.xml
$ xml sel -t -c //_:changeSet[_:createView[str:tokenize(@viewName,'')[last()]='v']] -n file.xml
$ xml sel -t -c //_:changeSet[_:createView[substring(@viewName,string-length(@viewName),1)='v']] -n file.xml
Основываясь на ответе @jens-erat ...
[substring(@viewName, string-length(@viewName)) = 'v']
можно сделать более СУХИМ, поместив предикат в атрибут @viewName:
[@viewName[substring(., string-length(.)) = 'v']]
в результате чего:
$ xmlstarlet sel -N x="http://www.liquibase.org/xml/ns/dbchangelog" -t -m \
"/x:databaseChangeLog/x:changeSet[x:createView
[@viewName[substring(., string-length(.)) = 'v']]
]" -c . public.db.changelog.xml