¿Cómo ejecutar XPath one-liners desde shell?

¿Hay algún paquete, para Ubuntu y / o CentOS, que tenga una herramienta de línea de comandos que pueda ejecutar un XPath one-liner como foo //element@attribute filename.xml o foo //element@attribute < filename.xml y devolver los resultados línea por línea?

Estoy buscando algo que me permita simplemente apt-get install foo o yum install foo y luego solo funciona de inmediato, sin envoltorios u otra adaptación necesaria.

Aquí hay algunos ejemplos de cosas que se acercan:

Nokogiri. Si escribo este contenedor podría llamar al contenedor de la manera descrita anteriormente:

 #!/usr/bin/ruby require 'nokogiri' Nokogiri::XML(STDIN).xpath(ARGV[0]).each do |row| puts row end 

XML :: XPath. Trabajaría con esta envoltura:

 #!/usr/bin/perl use strict; use warnings; use XML::XPath; my $root = XML::XPath->new(ioref => 'STDIN'); for my $node ($root->find($ARGV[0])->get_nodelist) { print($node->getData, "\n"); } 

xpath de XML :: XPath devuelve demasiado ruido, -- NODE -- y attribute = "value" .

xml_grep de XML :: Twig no puede manejar expresiones que no devuelven elementos, por lo que no se puede usar para extraer valores de atributos sin procesamiento adicional.

EDITAR:

echo cat //element/@attribute | xmllint --shell filename.xml echo cat //element/@attribute | xmllint --shell filename.xml devuelve un ruido similar a xpath .

xmllint --xpath //element/@attribute filename.xml devuelve attribute = "value" .

xmllint --xpath 'string(//element/@attribute)' filename.xml devuelve lo que quiero, pero solo para la primera coincidencia.

Para otra solución que casi satisface la pregunta, aquí hay un XSLT que se puede usar para evaluar expresiones XPath arbitrarias (requiere dyn: evaluar el soporte en el procesador XSLT):

           

Ejecutar con xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml .

Deberías probar estas herramientas:

  • xmlstarlet : puede editar, seleccionar, transformar … No está instalado de forma predeterminada, xpath1
  • xmllint : a menudo se instala de forma predeterminada con libxml2 , xpath1 (compruebe mi envoltorio para tener salidas delimitadas por líneas nuevas)
  • xpath : instalado a través del módulo de perl XML::XPath , xpath1
  • xml_grep : instalado a través del módulo de perl XML::Twig , xpath1 (uso limitado de xpath)
  • xidel : xpath3
  • saxon-lint : mi propio proyecto, envoltorio de la biblioteca de Saxon-HE Java de @Michael Kay, xpath3

xmllint viene con libxml2-utils (se puede usar como shell interactivo con el --shell )

xmlstarlet es xmlstarlet .

xpath viene con el módulo de perl XML::Xpath

xml_grep viene con el módulo de perl XML::Twig

xidel es xidel

saxon-lint con SaxonHE 9.6 , XPath 3.x (+ compatibilidad retro)

Ej .:

 xmllint --xpath '//element/@attribute' file.xml xmlstarlet sel -t -v "//element/@attribute" file.xml xpath -q -e '//element/@attribute' file.xml xidel -se '//element/@attribute' file.xml saxon-lint --xpath '//element/@attribute' file.xml 
  • página xmlstarlet
  • hombre xmllint
  • Página xpath
  • xml_grep
  • xidel
  • saxon-pelusa

.

Un paquete que es muy probable que esté instalado en un sistema ya es python-lxml . Si es así, esto es posible sin instalar ningún paquete adicional:

 python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))" 

También puedes probar mi Xidel . No está en un paquete en el repository, pero puedes descargarlo desde la página web (no tiene dependencias).

Tiene una syntax simple para esta tarea:

 xidel filename.xml -e '//element/@attribute' 

Y es una de las pocas herramientas que admite XPath 2.

Saxon hará esto no solo para XPath 2.0, sino también para XQuery 1.0 y (en la versión comercial) 3.0. No viene como un paquete de Linux, sino como un archivo jar. La syntax (que puede envolver fácilmente en una secuencia de comandos simple) es

 java net.sf.saxon.Query -s:source.xml -qs://element/attribute 

En mi búsqueda para consultar los archivos maven pom.xml me encontré con esta pregunta. Sin embargo, tenía las siguientes limitaciones:

  • debe ejecutar multiplataforma.
  • debe existir en todas las principales distribuciones de Linux sin ninguna instalación de módulo adicional
  • debe manejar archivos xml complejos como los archivos maven pom.xml
  • syntax simple

He intentado muchas de las anteriores sin éxito:

  • python lxml.etree no es parte de la distribución estándar de python
  • xml.etree es pero no maneja bien los archivos maven pom.xml complejos, no ha cavado lo suficientemente profundo
  • python xml.etree no maneja los archivos maven pom.xml por un motivo desconocido
  • xmllint tampoco funciona, los volcados de núcleo a menudo en ubuntu 12.04 “xmllint: utilizando libxml versión 20708”

La única solución con la que me he encontrado es estable, corta y que funciona en muchas plataformas y que está madura es la versión insertada de lib de rexml en ruby:

 ruby -r rexml/document -e 'include REXML; p XPath.first(Document.new($stdin), "/project/version/text()")' < pom.xml 

Lo que me inspiró a encontrar este fue los siguientes artículos:

  • Ruby / XML, XSLT y XPath Tutorial
  • IBM: Ruby on Rails y XML

También podría estar interesado en xsh . Presenta un modo interactivo donde puedes hacer lo que quieras con el documento:

 open 1.xml ; ls //element/@id ; for //p[@class="first"] echo text() ; 

La respuesta de clacke es genial, pero creo que solo funciona si tu fuente es XML bien formado, no HTML normal.

Entonces, haga lo mismo con el contenido web normal: documentos HTML que no son necesariamente XML bien formado:

 echo "

foo

bar

baz" | python -c "from sys import stdin; \ from lxml import html; \ print '\n'.join(html.tostring(node) for node in html.parse(stdin).xpath('//p'))"

Y, en su lugar, utilice html5lib (para asegurarse de obtener el mismo comportamiento de análisis que los navegadores web, porque al igual que los analizadores del navegador, html5lib cumple con los requisitos de análisis en las especificaciones HTML).

 echo "

foo

bar

baz" | python -c "from sys import stdin; \ import html5lib; from lxml import html; \ doc = html5lib.parse(stdin, treebuilder='lxml', namespaceHTMLElements=False); \ print '\n'.join(html.tostring(node) for node in doc.xpath('//p'))

Además de XML :: XSH y XML :: XSH2, hay algunas utilidades similares a grep chupan como App::xml_grep2 y XML::Twig (que incluye xml_grep lugar de xml_grep2 ). Estos pueden ser bastante útiles cuando se trabaja en un archivo XML grande o numeroso para los marcadores rápidos o los objectives Makefile . XML::Twig es especialmente bueno para trabajar con un enfoque de scripting de perl cuando desea un procesamiento un poco más que su $SHELL y xmllint xstlproc .

El esquema de numeración en los nombres de las aplicaciones indica que las versiones “2” son versiones más nuevas / posteriores de esencialmente la misma herramienta que puede requerir versiones posteriores de otros módulos (o del propio perl ).

Similar a las respuestas de Mike y Clacke, aquí está el one-liner de python (usando python> = 2.5) para obtener la versión de comstackción de un archivo pom.xml que evita el hecho de que los archivos pom.xml normalmente no tienen un dtd o espacio de nombres predeterminado, por lo que no aparece bien formado a libxml:

 python -c "import xml.etree.ElementTree as ET; \ print(ET.parse(open('pom.xml')).getroot().find('\ {http://maven.apache.org/POM/4.0.0}version').text)" 

Probado en Mac y Linux, y no requiere ningún paquete adicional para ser instalado.

Cabe mencionar que el propio nokogiri se envía con una herramienta de línea de comandos, que debe instalarse con gem install nokogiri .

Es posible que esta publicación de blog le resulte útil .

He probado un par de utilidades XPath de línea de comandos y cuando me di cuenta de que estaba pasando demasiado tiempo buscando en Google y averiguando cómo funcionan, escribí el analizador XPath más simple posible en Python, que hizo lo que necesitaba.

El siguiente script muestra el valor de la cadena si la expresión XPath se evalúa como una cadena, o muestra el subnodo XML completo si el resultado es un nodo:

 #!/usr/bin/env python import sys from lxml import etree tree = etree.parse(sys.argv[1]) xpath = sys.argv[2] for e in tree.xpath(xpath): if isinstance(e, str): print(e) else: print((e.text and e.text.strip()) or etree.tostring(e)) 

Utiliza lxml , un analizador XML rápido escrito en C que no está incluido en la biblioteca de python estándar. Instalarlo con pip install lxml . En Linux / OSX podría necesitar un prefijo con sudo .

Uso:

 python xmlcat.py file.xml "//mynode" 

lxml también puede aceptar una URL como entrada:

 python xmlcat.py http://example.com/file.xml "//mynode" 

Extraiga el atributo url debajo de un nodo de alojamiento, por ejemplo, ) :

 python xmlcat.py xmlcat.py file.xml "//enclosure/@url" 

Xpath en Google Chrome

Como nota al margen no relacionada: si por casualidad desea ejecutar una expresión XPath contra el marcado de una página web, puede hacerlo directamente desde las herramientas devto de Chrome: haga clic con el botón derecho en la página en Chrome> seleccione Inspeccionar y luego en DevTools consola pegue su expresión XPath como $x("//spam/eggs") .

Obtenga todos los autores en esta página:

 $x("//*[@class='user-details']/a/text()") 

Dado que este proyecto aparentemente es bastante nuevo, consulte https://github.com/jeffbr13/xq , parece ser un contenedor alrededor de lxml , pero eso es todo lo que realmente necesita (y publicó soluciones ad hoc usando lxml en otras respuestas, así )

Aquí hay un caso de uso xmlstarlet para extraer datos de los elementos nesteds elem1, elem2 a una línea de texto de este tipo de XML (que también muestra cómo manejar los espacios de nombres):

 < ?xml version="1.0" encoding="UTF-8" standalone="yes" ?>      

La salida será

 0.586 10.586 cue-in outro 

En este fragmento, -m coincide con los valores de atributo de salidas elem2, -v nesteds (con expresiones y direccionamiento relativo), -o texto literal, -n agrega una nueva línea:

 xml sel -N ns="http://xml-namespace-uri" -t -m '//ns:elem1/ns:elem2' \ -v ../@time -o " " -v '../@time + ../@length' -o " " -v @value -o " " -v @type -n file.xml 

Si se necesitan más atributos de elem1, uno puede hacerlo así (también muestra la función concat ()):

 xml sel -N ns="http://xml-namespace-uri" -t -m '//ns:elem1/ns:elem2/..' \ -v 'concat(@time, " ", @time + @length, " ", ns:elem2/@value, " ", ns:elem2/@type)' -n file.xml 

Nótese la complicación (IMO innecesaria) con espacios de nombres (ns, declarados con -N), que me tenían casi abandonando en xpath y xmlstarlet, y escribiendo un rápido conversor ad-hoc.