Como executar XPath one-liners da shell?

existe um pacote lá fora, Para Ubuntu e / ou CentOS, que tem uma ferramenta de linha de comando que pode executar um XPath de um liner como foo //element@attribute filename.xml ou foo //element@attribute < filename.xml e devolver os resultados linha por linha?

Estou à procura de uma coisa que me permita apenas ... ou ... e depois funciona fora da caixa, sem invólucros ou outra adaptação necessária.

Aqui estão alguns exemplos de coisas que se aproximam:

Nokogiri. Se eu escrever este papel eu poderia chamar o papel no modo descrito acima:

#!/usr/bin/ruby

require 'nokogiri'

Nokogiri::XML(STDIN).xpath(ARGV[0]).each do |row|
  puts row
end

XML:: XPath. Trabalharia com esta embalagem.

#!/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 do XML:: XPath devolve muito ruído, -- NODE -- e attribute = "value".

xml_grep a partir de XML::Twig não consegue lidar com expressões que não devolvem elementos, por isso não pode ser usado para extrair valores de atributos sem processamento adicional.

editar:

echo cat //element/@attribute | xmllint --shell filename.xml devolve um ruído semelhante ao xpath.

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

xmllint --xpath 'string(//element/@attribute)' filename.xml devolve o que eu quero, mas só para o primeiro jogo.

para outra solução que quase satisfaça a questão, aqui está um XSLT que pode ser usado para avaliar expressões XPath arbitrárias (requer dyn: avaliar o suporte no processador XSLT):

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn">
  <xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
  <xsl:template match="/">
    <xsl:for-each select="dyn:evaluate($pattern)">
      <xsl:value-of select="dyn:evaluate($value)"/>
      <xsl:value-of select="'&#10;'"/>
    </xsl:for-each> 
  </xsl:template>
</xsl:stylesheet>

corre com xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml.

Author: Mike, 2013-03-17

13 answers

Devias experimentar estas ferramentas:

  • xmlstarlet: pode editar, seleccionar, transformar... Não está instalado por omissão, xpath1
  • xmllint: frequentemente instalado por omissão com libxml2, xpath1( verifique a minha embalagem para ter as linhas novas delimitadas de saída
  • xpath: instalado através do módulo do perl XML::XPath, xpath1
  • xml_grep: instalado através do módulo do perl XML::Twig, xpath1 (utilização limitada do xpath)
  • xidel: xpath3
  • O meu próprio projecto, embrulhado. @ Michael Kay's Saxon-HE Java library, xpath3

xmllint vem com libxml2-utils (pode ser usado como shell interactivo com o interruptor --shell)

xmlstarlet riz[1]}.

xpath vem com o módulo do perlXML::Xpath

xml_grep vem com o módulo do perlXML::Twig

xidel é xidel

saxon-lint usando SaxonHE 9.6 ,XPath 3.x (+compatibilidade retroactiva)

Ex:

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

.

 205
Author: Gilles Quenot, 2018-03-25 17:16:31

Um pacote que é muito provável ser instalado num sistema já é python-lxml. Em caso afirmativo, isto é possível sem a instalação de qualquer pacote extra:

python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))"
 15
Author: clacke, 2013-03-18 07:53:00

Também podes experimentar o meuXidel . Ele não está em um pacote no repositório, mas você pode apenas baixá-lo a partir da página web (ele não tem dependências).

Tem uma sintaxe simples para esta tarefa:

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

E é uma das raras ferramentas que suporta XPath 2.

 15
Author: BeniBela, 2016-10-15 19:38:01

Saxon fará isso não só para XPath 2.0, mas também para XQuery 1.0 e (na versão comercial) 3.0. Ele não vem como um pacote Linux, mas como um arquivo jar. A sintaxe (que pode facilmente envolver num programa simples) é

java net.sf.saxon.Query -s:source.xml -qs://element/attribute
 10
Author: Michael Kay, 2013-03-17 16:55:38
Na minha Busca por Maven pom.os ficheiros xml que analisei abordam esta questão. No entanto, eu tinha as seguintes limitações:
    Tenho de atravessar a plataforma.
  • deve existir em todas as principais distribuições linux sem qualquer instalação adicional de módulos
  • deve lidar com ficheiros xml complexos como o Maven pom.ficheiros xml
  • sintaxe simples

Tentei muitos dos acima sem sucesso:

  • python lxml.o etree não faz parte do python padrão. distribuição
  • xml.etree é mas não lida com o complexo Maven pom.ficheiros xml bem, não foram suficientemente enterrados
  • xml python.etree não lida com maven pom.ficheiros xml por razão desconhecida
  • o xmllint também não funciona, o core dumps frequentemente no ubuntu 12.04 "xmllint: usando a libxml versão 20708"

A única solução que encontrei que é estável, curta e que funciona em muitas plataformas e que é madura é o rexml lib builtin em ruby:

ruby -r rexml/document -e 'include REXML; 
     p XPath.first(Document.new($stdin), "/project/version/text()")' < pom.xml
O que foi? inspirou-me a descobrir que estes eram os seguintes artigos:
 9
Author: Mike, 2015-07-25 14:20:50

Também pode estar interessado em xsh. Ele possui um modo interativo onde você pode fazer o que quiser com o documento:

open 1.xml ;
ls //element/@id ;
for //p[@class="first"] echo text() ;
 5
Author: choroba, 2015-02-26 09:50:43

A resposta de Clacke é óptima, mas acho que só funciona se a sua fonte FOR XML bem formada, Não HTML normal.

De modo a fazer o mesmo para o conteúdo web normal-Docs HTML que não são necessariamente bem formados XML:

echo "<p>foo<div>bar</div><p>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'))"

E, em vez disso, usar html5lib (para garantir que você tem o mesmo comportamento de análise que os navegadores Web-porque, como os navegadores, html5lib está em conformidade com os requisitos de análise no HTML spec).

echo "<p>foo<div>bar</div><p>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'))
 4
Author: sideshowbarker, 2017-05-23 12:34:41

Além de XML::XSH e XML::XSH2 há alguns grep-como utilitários de chupar como App::xml_grep2 e XML::Twig (o que inclui xml_grep em vez de xml_grep2). Estes podem ser bastante úteis ao trabalhar em um grande ou numerosos arquivos XML para oneliners rápidos ou alvos Makefile. XML::Twig é especialmente bom trabalhar com uma abordagem de scripting perl quando você quer um pouco mais de processamento do que o seu $SHELL e xmllint xstlproc oferta.

O sistema de numeração nos nomes das aplicações indica que as versões" 2 " são versões mais recentes/posteriores essencialmente da mesma ferramenta que pode exigir versões posteriores de outros módulos (ou do próprio perl).

 2
Author: G. Cito, 2014-03-05 16:37:31

Similar às respostas de Mike e clacke, aqui está o python one-liner (usando python > = 2.5) para obter a versão de compilação de um pom.ficheiro xml que contorna o facto do pom.os ficheiros xml não têm normalmente um dtd ou um espaço de nomes predefinido, por isso não aparecem bem formados na 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)"

Testado em Mac e Linux, e não requer nenhum pacote extra para ser instalado.

 2
Author: pdr, 2015-03-20 23:53:24

É preciso mencionar que o nokogiri em si navega com uma ferramenta de linha de comando, que deve ser instalada com {[[0]}.

Você pode achar este post útil.

 2
Author: Geoff Nixon, 2015-12-23 07:29:57
Tentei alguns utilitários XPath da linha de comando e quando percebi que estou a passar muito tempo a pesquisar e a descobrir como funcionam, escrevi o mais simples possível processador XPath em Python que fez o que eu precisava.

O programa abaixo mostra o valor do texto se a expressão XPath for avaliada para um texto, ou mostra todo o subnodo XML se o resultado for um nó:

#!/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))

Ele usa lxml - um analisador XML rápido escrito em C que não está incluído na norma biblioteca python. Instale-o com pip install lxml. No Linux / OSX poderá ser necessário o prefixo com sudo.

Utilização:

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

O Lxml também pode aceitar um URL como entrada:

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

Extrair o atributo url sob um nó de confinamento, i.e. <enclosure url="http:...""..>):

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

Xpath no Google Chrome

Como uma nota lateral não relacionada: se por acaso quiser executar uma expressão de XPath contra a marcação de uma página web, então poderá fazê-lo directamente a partir dos devtools cromados: carregue com o botão direito na página no Chrome > seleccione a opção inspeccionar e, em seguida, na consola DevTools cole a sua expressão de XPath como $x("//spam/eggs").

Põe todos os autores nesta página:

$x("//*[@class='user-details']/a/text()")
 1
Author: ccpizza, 2018-02-05 00:37:56

, uma vez que este projecto é, aparentemente, bastante novo, confira https://github.com/jeffbr13/xq , parece ser um wrapper lxml, mas isso é tudo que você realmente precisa (e postado soluções ad hoc, usando twofish, tiger em outras respostas)

 1
Author: mgrandi, 2018-04-20 00:06:40

Aqui está um caso de uso de xmlstarlet para extrair dados de elementos aninhados elem1, elem2 para uma linha de texto deste tipo de XML (também mostrando como lidar com espaços de nomes):

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<mydoctype xmlns="http://xml-namespace-uri" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml-namespace-uri http://xsd-uri" format="20171221A" date="2018-05-15">

  <elem1 time="0.586" length="10.586">
      <elem2 value="cue-in" type="outro" />
  </elem1>

</mydoctype>

A saída será

0.586 10.586 cue-in outro

Neste excerto, - m corresponde aos valores dos atributos dos resultados aninhados (com expressões e endereçamento relativo), -o texto literal, -n adiciona uma nova linha:

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

Se são necessários mais atributos do elem1, pode-se fazê-lo assim (também mostrando o concat () função):

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

Note a complicação (desnecessária) com espaços de nomes (ns, declarado com-N), que me fez quase desistir do xpath e do xmlstarlet, e escrever um rápido conversor ad-hoc.

 0
Author: diemo, 2018-05-17 11:24:55