Equivalente Grep e Sed para processamento de linha de comando XML

147

Ao executar scripts de shell, normalmente os dados estarão em arquivos de registros de linha única como o csv. É realmente simples manipular esses dados com grepe sed. Mas eu tenho que lidar com XML frequentemente, então eu realmente gostaria de uma maneira de criar um script de acesso a esses dados XML através da linha de comando. Quais são as melhores ferramentas?

Joseph Holsten
fonte
xml_grep é adequado para grepping, conforme declarado em stackoverflow.com/a/2222224/871134
Deleplace

Respostas:

105

Eu achei o xmlstarlet muito bom nesse tipo de coisa.

http://xmlstar.sourceforge.net/

Também deve estar disponível na maioria dos repositórios de distribuição. Um tutorial introdutório está aqui:

http://www.ibm.com/developerworks/library/x-starlet.html

Russ
fonte
1
Pensei em salientar que existem binários do Windows disponíveis no site Sourceforge.
9788 Steve Jobs,
Não suporta XQuery, tanto quanto eu posso dizer.
Steve Bennett
@SteveBennett, de fato, não, mas os recursos adicionados ao XPath bruto são bons o suficiente para torná-lo competitivo com o "grep e sed". Se você deseja a sofisticada e sofisticada bondade do XQuery ... bem, é mais como um XML equivalente a perl ou awk. :)
Charles Duffy
36

Algumas ferramentas promissoras:

  • nokogiri : analisando DOMs HTML / XML em ruby ​​usando seletores XPath e CSS

  • hpricot : descontinuado

  • fxgrep : Usa sua própria sintaxe semelhante ao XPath para consultar documentos. Escrito em SML, a instalação pode ser difícil.

  • LT XML : kit de ferramentas XML derivada de ferramentas SGML, incluindo sggrep, sgsort, xmlnorme outros. Usa sua própria sintaxe de consulta. A documentação é muito formal. Escrito em C. O LT XML 2 alega suporte ao XPath, XInclude e outros padrões W3C.

  • xmlgrep2 : pesquisa simples e poderosa com o XPath. Escrito em Perl usando XML :: LibXML e libxml2.

  • XQSharp : Suporta XQuery, a extensão para XPath. Escrito para o .NET Framework.

  • xml-coreutils : kit de ferramentas do Laird Breyer equivalente ao GNU coreutils. Discutido em um ensaio interessante sobre o que o kit de ferramentas ideal deve incluir.

  • xmldiff : ferramenta simples para comparar dois arquivos xml.

  • xmltk : parece não ter pacote no debian, ubuntu, fedora ou macports, não é lançado desde 2007 e usa automação de compilação não portátil.

O xml-coreutils parece o melhor documentado e o mais orientado para UNIX.

Joseph Holsten
fonte
1
Não foi possível criar um script de wrapper para o programa Ruby e passar a matriz dos argumentos no script para hpricot? Por exemplo, em um script de shell PHP, algo como o seguinte deve funcionar: <? Php / path / to / hpricot $ argv?>
alastairs
25

À excelente lista de Joseph Holsten, adiciono o script de linha de comando xpath que acompanha a biblioteca Perl XML :: XPath. Uma ótima maneira de extrair informações de arquivos XML:

 xpath -q -e '/entry[@xml:lang="fr"]' *xml
bortzmeyer
fonte
3
Isso é instalado por padrão no osx, mas sem -q -eopções. Exemplo, obtenha o valor do "pacote" do atributo no nó "manifesto" em "AndroidManifest.xml":xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
antonj
25

Há também xml2e 2xmlpar. Isso permitirá que as ferramentas comuns de edição de cadeias processem XML.

Exemplo. q.xml:

<?xml version="1.0"?>
<foo>
    text
    more text
    <textnode>ddd</textnode><textnode a="bv">dsss</textnode>
    <![CDATA[ asfdasdsa <foo> sdfsdfdsf <bar> ]]>
</foo>

xml2 < q.xml

/foo=
/foo=   text
/foo=   more text
/foo=   
/foo/textnode=ddd
/foo/textnode
/foo/textnode/@a=bv
/foo/textnode=dsss
/foo=
/foo=    asfdasdsa <foo> sdfsdfdsf <bar> 
/foo=

xml2 < q.xml | grep textnode | sed 's!/foo!/bar/baz!' | 2xml

<bar><baz><textnode>ddd</textnode><textnode a="bv">dsss</textnode></baz></bar>

PS Há também html2/ 2html.

Vi.
fonte
@Joseph Holsten Sim. Ele permite hackers com XML sem pensar nas coisas do XPath.
Vi.
Agradável! Eu estava focando em ferramentas que não usam um formato intermediário, mas a ideia de uma representação de xml de alta fidelidade e orientada a linhas parece ser uma ótima maneira de continuar usando grep e sed reais. Você já experimentou pyxie? Como se compara? Alguma outra representação orientada a linhas? Você consideraria isso melhor do que apenas substituir novas linhas xml por uma entidade (& # 10;)? Isso permitiria que você cole registros na mesma linha, pelo menos. Ah, e você poderia editar sua postagem para incluir um link para o projeto?
Joseph Holsten
@ Joseph Holsten Não, não acho que o formato pyxie seja mais útil que o formato xml2. O xml2 fornece "caminho completo" em elementos XML aninhados, portanto, permite mais correspondência e substituição orientada a linhas. Também 2xmlpode recriar facilmente XML a partir de xml2saída parcial (filtrada) .
Vi.
5
+1 Não consigo votar mais o suficiente ... cat foo.xml | xml2 | grep /bar | 2xml- fornece a mesma estrutura que o original, mas todos os elementos foram removidos, exceto os elementos "bar". Impressionante.
mogsie
14

Você pode usar xmllint:

xmllint --xpath //title books.xml

Deve ser fornecido com a maioria das distribuições e também com o Cygwin.

$ xmllint --version
xmllint: using libxml version 20900

Vejo:

$ xmllint
Usage : xmllint [options] XMLfiles ...
        Parse the XML files and output the result of the parsing
        --version : display the version of the XML library used
        --debug : dump a debug tree of the in-memory document
        ...
        --schematron schema : do validation against a schematron
        --sax1: use the old SAX1 interfaces for processing
        --sax: do not build a tree but work just at the SAX level
        --oldxml10: use XML-1.0 parsing rules before the 5th edition
        --xpath expr: evaluate the XPath expression, inply --noout
Dave Jarvis
fonte
2
Não há --xpathargumento para xmllint: manpagez.com/man/1/xmllint
Variable Miserable
1
@MiserableVariable: a página de manual está incorreta. Acabei de olhar a página de manual da minha versão: o argumento xpath não está listado. Este é um erro de documentação. Tente executar o programa.
Dave Jarvis
2
@MiserableVariable --xpathé uma adição relativamente recente e, por exemplo, não nas versões RHEL 6 do xmllint.
Daniel Beck
2
Para ser mais preciso, xmllint --xpathfoi introduzido no libxml2 2.7.7 (em 2010).
marbu
9

Se você está procurando uma solução no Windows, o Powershell possui uma funcionalidade integrada para ler e escrever XML.

test.xml:

<root>
  <one>I like applesauce</one>
  <two>You sure bet I do!</two>
</root>

Script do PowerShell:

# load XML file into local variable and cast as XML type.
$doc = [xml](Get-Content ./test.xml)

$doc.root.one                                   #echoes "I like applesauce"
$doc.root.one = "Who doesn't like applesauce?"  #replace inner text of <one> node

# create new node...
$newNode = $doc.CreateElement("three")
$newNode.set_InnerText("And don't you forget it!")

# ...and position it in the hierarchy
$doc.root.AppendChild($newNode)

# write results to disk
$doc.save("./testNew.xml")

testNew.xml:

<root>
  <one>Who likes applesauce?</one>
  <two>You sure bet I do!</two>
  <three>And don't you forget it!</three>
</root>

Fonte: /server/26976/update-xml-from-the-command-line-windows

Argila
fonte
lutou com várias ferramentas Linux por algumas horas antes de recorrer ao Powershell. Estou surpreso que isso seja tão difícil - o linux cmd-line normalmente é realmente bom, mas parece haver um buraco aqui. Nota: O caso de uso para mim foi: 1) localize os nós pelo xpath, 2) remova se for encontrado, 3) adicione novos nós, 4) salve o arquivo. Eu estava atualizando um monte de configurações de solr. Se alguém souber de uma maneira fácil / confiável de fazer isso, sou todo ouvidos #
Richard Hauer
Uau, isso realmente chega aos pés da linha de uma solução aceitável. Mas, honestamente, eu provavelmente aceitaria se parecesse xps $doc .root.one xps $doc 'AppendChild("three")'e xps $doc '.three.set_InnerText("And don't you forget it!")', o que é claramente inferior!
Joseph Holsten
6

Depende exatamente do que você deseja fazer.

O XSLT pode ser o caminho a seguir, mas há uma curva de aprendizado. Tente xsltproc e observe que você pode entregar parâmetros.

Adrian Mouat
fonte
4

Também existe na saxon-lintlinha de comando a capacidade de usar o XPath 3.0 / XQuery 3.0. (Outras ferramentas de linha de comando usam o XPath 1.0).

EXEMPLOS :

http / html:

$ saxon-lint --html --xpath 'count(//a)' http://stackoverflow.com/q/91791
328

xml:

$ saxon-lint --xpath '//a[@class="x"]' file.xml
Gilles Quenot
fonte
4

D. Bohdan mantém um repositório de código-fonte aberto do GitHub que mantém uma lista de ferramentas de linha de comando para ferramentas de texto estruturado; há uma seção para ferramentas XML / HTML:

https://github.com/dbohdan/structured-text-tools#xml-html

Devy
fonte
3

XQuery pode ser uma boa solução. É (relativamente) fácil de aprender e é um padrão W3C.

Eu recomendaria o XQSharp para um processador de linha de comando.

Oliver Hallam
fonte
1
O BaseX também possui um processador XQuery de linha de comando (além do modo de banco de dados) e fica atualizado com as versões mais avançadas do padrão (seguindo o rascunho em evolução do XQuery 3.0).
Charles Duffy
3

Eu usei o xmlstarlet pela primeira vez e ainda o uso. Quando a consulta fica difícil, preciso do suporte aos recursos xpath2 e xquery do XML. Volto- me para xidel http://www.videlibri.de/xidel.html

truthadjustr
fonte
1

Equivalente Grep

Você pode definir uma função bash, digamos "xp" ("xpath") que envolve algum código python3. Para usá-lo, você precisa instalar python3 e python-lxml. Benefícios:

  1. correspondência de regex que você não possui, por exemplo, xmllint.
  2. Use como um filtro (em um canal) na linha de comando

É fácil e poderoso de usar assim:

xmldoc=$(cat <<EOF
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
EOF
)
selection='//*[namespace-uri()="http://www.sample.com/" and local-name()="job" and re:test(.,"^pro.*ing$")]/text()'
echo "$xmldoc" | xp "$selection"
# prints programming

xp () se parece com isso:

xp()
{ 
local selection="$1";
local xmldoc;
if ! [[ -t 0 ]]; then
    read -rd '' xmldoc;
else
    xmldoc="$2";
fi;
python3 <(printf '%b' "from lxml.html import tostring\nfrom lxml import etree\nfrom sys import stdin\nregexpNS = \"http://exslt.org/regular-expressions\"\ntree = etree.parse(stdin)\nfor e in tree.xpath('""$selection""', namespaces={'re':regexpNS}):\n  if isinstance(e, str):\n    print(e)\n  else:\n    print(tostring(e).decode('UTF-8'))") <<< "$xmldoc"
}

Sed Equivalent

Considere o uso do xq, que fornece todo o poder da "linguagem de programação" jq. Se você possui o python-pip instalado, pode instalar o xq com o pip install yq ; no exemplo abaixo, estamos substituindo "Manter contas" por "Manter contas 2":

xmldoc=$(cat <<'EOF'
<resources>
    <string name="app_name">Keep Accounts</string>
    <string name="login">"login"</string>
    <string name="login_password">"password:"</string>
    <string name="login_account_hint">input to login</string>
    <string name="login_password_hint">input your password</string>
    <string name="login_fail">login failed</string>
</resources>
EOF
)
echo "$xmldoc" | xq '.resources.string = ([.resources.string[]|select(."#text" == "Keep Accounts") ."#text" = "Keep Accounts 2"])' -x
methuselah-0
fonte
-1

O JEdit possui um plugin chamado "XQuery", que fornece funcionalidade de consulta para documentos XML.

Não é bem a linha de comando, mas funciona!

Ben
fonte
Embora o JEdit provavelmente tenha uma maneira de pesquisar em um arquivo, isso não o torna um concorrente grep(1).
Joseph Holsten