Grep e Sed equivalenti per l'elaborazione da riga di comando XML


147

Quando si eseguono script di shell, in genere i dati si trovano in file di record a riga singola come CSV. È davvero semplice gestire questi dati con grepe sed. Ma devo occuparmi spesso di XML, quindi mi piacerebbe davvero un modo per accedere agli script di quei dati XML tramite la riga di comando. Quali sono gli strumenti migliori?


xml_grep va bene per grep, come indicato nel stackoverflow.com/a/2222224/871134
Deleplace

Risposte:


105

Ho trovato xmlstarlet per essere abbastanza bravo in questo genere di cose.

http://xmlstar.sourceforge.net/

Dovrebbe essere disponibile anche nella maggior parte dei repository di distro. Un tutorial introduttivo è qui:

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


1
Ho pensato di sottolineare che ci sono file binari di Windows disponibili sul sito Sourceforge.
Steve Bennett,

Tuttavia, per quanto ne so, non supporta XQuery.
Steve Bennett,

@SteveBennett in effetti non lo è, ma le funzionalità che aggiunge a XPath non elaborate sono abbastanza buone da renderlo competitivo con "grep e sed". Se vuoi la bontà fantasiosa di XQuery ... beh, è ​​più simile a un XML equivalente a perl o awk. :)
Charles Duffy,

36

Alcuni strumenti promettenti:

  • nokogiri : analisi di DOM HTML / XML in ruby ​​utilizzando selettori XPath e CSS

  • hpricot : deprecato

  • fxgrep : utilizza la propria sintassi simile a XPath per eseguire query sui documenti. Scritto in SML, quindi l'installazione potrebbe essere difficile.

  • LT XML : toolkit XML derivato da strumenti SGML, tra cui sggrep, sgsort, xmlnorme altri. Utilizza la propria sintassi della query. La documentazione è molto formale. Scritto in C. LT XML 2 rivendica il supporto di XPath, XInclude e altri standard W3C.

  • xmlgrep2 : ricerca semplice e potente con XPath. Scritto in Perl usando XML :: LibXML e libxml2.

  • XQSharp : supporta XQuery, l'estensione di XPath. Scritto per .NET Framework.

  • xml-coreutils : toolkit di Laird Breyer equivalente ai coreutils GNU. Discusso in un saggio interessante su cosa dovrebbe includere il toolkit ideale.

  • xmldiff : semplice strumento per confrontare due file xml.

  • xmltk : non sembra avere un pacchetto in debian, ubuntu, fedora o macports, non è stato rilasciato dal 2007 e usa un'automazione di build non portatile.

xml-coreutils sembra il meglio documentato e orientato a UNIX.


1
Non potresti creare uno script wrapper per il programma Ruby e passare la matrice degli argomenti nello script a hpricot? Ad esempio, in uno script shell PHP, dovrebbe funzionare qualcosa di simile al seguente: <? Php / path / to / hpricot $ argv?>
alastairs,

25

All'eccellente elenco di Joseph Holsten, aggiungo lo script da riga di comando xpath fornito con la libreria Perl XML :: XPath. Un ottimo modo per estrarre informazioni dai file XML:

 xpath -q -e '/entry[@xml:lang="fr"]' *xml

3
Questo è installato di default in osx, ma senza -q -eopzioni. Esempio, ottenere il valore "pacchetto" dell'attributo dal nodo "manifest" in "AndroidManifest.xml":xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
antonj

25

C'è anche xml2e 2xmlcoppia. Consentirà ai soliti strumenti di modifica delle stringhe di elaborare XML.

Esempio. 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 Ci sono anche html2/ 2html.


@Joseph Holsten Sì. Permette l'hacking con XML senza pensare alle cose di XPath.
Vi.

Bello! Mi ero concentrato su strumenti che non usano un formato intermedio, ma l'idea di una rappresentazione xml ad alta fedeltà e orientata alla linea sembra un ottimo modo per continuare a usare grep e sed reali. Hai provato Pyxie? Come si confronta? Altre rappresentazioni orientate alla linea? Lo considereresti migliore della semplice sostituzione di newline xml con un'entità (& # 10;)? Ciò ti consentirebbe di incollare almeno i record sulla stessa riga. Oh, e potresti modificare il tuo post per includere un link al progetto?
Joseph Holsten,

@Joseph Holsten No, non credo che il formato pyxie sarebbe più utile del formato xml2. xml2 fornisce "percorso completo" negli elementi XML nidificati, quindi consente una corrispondenza e una sostituzione più orientate alla linea. Inoltre 2xmlpuò facilmente ricreare XML xml2dall'output parziale (filtrato) .
Vi.

5
+1 Non posso votare abbastanza questo ... cat foo.xml | xml2 | grep /bar | 2xml- ti dà la stessa struttura dell'originale, ma tutti gli elementi sono stati rimossi tranne gli elementi "bar". Eccezionale.
mogsie,

14

Puoi usare xmllint:

xmllint --xpath //title books.xml

Dovrebbe essere in bundle con la maggior parte delle distro, ed è anche in bundle con Cygwin.

$ xmllint --version
xmllint: using libxml version 20900

Vedere:

$ 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

2
Non ci sono --xpathargomenti per xmllint: manpagez.com/man/1/xmllint
Miserable Variable

1
@MiserableVariable: la pagina man non è corretta. Ho appena guardato la pagina man per la mia versione: l'argomento xpath non è elencato. Questo è un errore di documentazione. Prova invece a eseguire il programma.
Dave Jarvis,

2
@MiserableVariable --xpathè un'aggiunta abbastanza recente e, ad esempio, non nelle versioni RHEL 6 di xmllint.
Daniel Beck,

2
Per essere più precisi, è xmllint --xpathstato introdotto in libxml2 2.7.7 (nel 2010).
marbu,

9

Se stai cercando una soluzione su Windows, Powershell ha funzionalità integrate per la lettura e la scrittura di XML.

test.xml:

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

Script 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


combattuto con vari strumenti linux per alcune ore prima di ricorrere a Powershell. Sono sorpreso che sia così difficile - Linux cmd-line è normalmente davvero buono ma sembra che ci sia un buco qui. Nota: il caso d'uso per me era: 1) individua i nodi con xpath, 2) rimuovi se trovato, 3) aggiungi nuovi nodi, 4) salva il file. Stavo aggiornando un sacco di configurazioni solr. Se qualcuno conosce un modo semplice / affidabile per farlo, sono tutto orecchi
Richard Hauer,

Wow, questo è davvero in punta di piedi fino alla linea di una soluzione accettabile. Ma onestamente, probabilmente lo accetterei se sembrava xps $doc .root.one xps $doc 'AppendChild("three")'e xps $doc '.three.set_InnerText("And don't you forget it!")', che è chiaramente inferiore!
Joseph Holsten,


6

Dipende esattamente da cosa vuoi fare.

XSLT può essere la strada da percorrere, ma c'è una curva di apprendimento. Prova xsltproc e nota che puoi consegnare i parametri.


4

C'è anche saxon-lintdalla riga di comando con la possibilità di utilizzare XPath 3.0 / XQuery 3.0. (Altri strumenti da riga di comando utilizzano XPath 1.0).

ESEMPI:

http / html:

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

xml:

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


3

XQuery potrebbe essere una buona soluzione. È (relativamente) facile da imparare ed è uno standard W3C.

Consiglierei XQSharp per un processore da riga di comando.


1
BaseX ha anche un processore XQuery da riga di comando (oltre alla modalità database) e rimane aggiornato con le versioni all'avanguardia dello standard (seguendo da vicino la bozza in evoluzione di XQuery 3.0).
Charles Duffy,


1

Grep Equivalent

È possibile definire una funzione bash, dire "xp" ("xpath") che avvolge del codice python3. Per usarlo è necessario installare python3 e python-lxml. Benefici:

  1. corrispondenza regex che manca ad es. xmllint.
  2. Utilizzare come filtro (in una pipe) sulla riga di comando

È facile e potente da usare in questo modo:

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 () ha un aspetto simile al seguente:

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 equivalente

Prendi in considerazione l'uso di xq che ti dà tutta la potenza del "linguaggio di programmazione" jq. Se hai installato python-pip, puoi installare xq con pip install yq , quindi nell'esempio seguente sostituiamo "Mantieni account" con "Mantieni account 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

-1

JEdit ha un plugin chiamato "XQuery" che fornisce funzionalità di interrogazione per documenti XML.

Non proprio la riga di comando, ma funziona!


Sebbene JEdit abbia probabilmente un modo per cercare un file, ciò non lo rende un concorrente grep(1).
Joseph Holsten,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.