Grep- und Sed-Äquivalent für die XML-Befehlszeilenverarbeitung


147

Beim Shell-Scripting befinden sich Daten normalerweise in Dateien mit einzeiligen Datensätzen wie csv. Es ist wirklich einfach, diese Daten mit grepund zu verarbeiten sed. Da ich mich jedoch häufig mit XML befassen muss, möchte ich wirklich einen Skriptzugriff auf diese XML-Daten über die Befehlszeile durchführen. Was sind die besten Werkzeuge?


xml_grep ist gut für Grepping, wie in stackoverflow.com/a/2222224/871134
Deleplace

Antworten:


105

Ich habe festgestellt, dass xmlstarlet in solchen Dingen ziemlich gut ist.

http://xmlstar.sourceforge.net/

Sollte auch in den meisten Distributions-Repositories verfügbar sein. Ein einführendes Tutorial finden Sie hier:

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


1
Ich dachte, ich möchte darauf hinweisen, dass auf der Sourceforge-Site Windows-Binärdateien verfügbar sind.
Steve Bennett

Unterstützt XQuery jedoch nicht, soweit ich das beurteilen kann.
Steve Bennett

@SteveBennett tut es zwar nicht, aber die Funktionen, die es zusätzlich zu rohem XPath hinzufügt, sind gut genug, um es mit "grep and sed" konkurrenzfähig zu machen. Wenn Sie die schicke, schicke Güte von XQuery wollen ... nun, das ist eher ein XML-Äquivalent zu Perl oder Awk. :)
Charles Duffy

36

Einige vielversprechende Tools:

  • nokogiri : Analysieren von HTML / XML-DOMs in Ruby mithilfe von XPath- und CSS-Selektoren

  • hpricot : veraltet

  • fxgrep : Verwendet eine eigene XPath-ähnliche Syntax zum Abfragen von Dokumenten. In SML geschrieben, daher kann die Installation schwierig sein.

  • LT XML : XML - Toolkit von SGML - Tool abgeleitet, einschließlich sggrep, sgsort, xmlnormund andere. Verwendet eine eigene Abfragesyntax. Die Dokumentation ist sehr formal. In C. LT XML 2 wird die Unterstützung von XPath, XInclude und anderen W3C-Standards beansprucht.

  • xmlgrep2 : einfache und leistungsstarke Suche mit XPath. Geschrieben in Perl mit XML :: LibXML und libxml2.

  • XQSharp : Unterstützt XQuery, die Erweiterung von XPath. Geschrieben für .NET Framework.

  • xml-coreutils : Laird Breyers Toolkit, das GNU-Coreutils entspricht. In einem interessanten Aufsatz darüber diskutiert, was das ideale Toolkit enthalten sollte.

  • xmldiff : Einfaches Tool zum Vergleichen von zwei XML-Dateien.

  • xmltk : scheint kein Paket in Debian, Ubuntu, Fedora oder Macports zu haben, hat seit 2007 keine Veröffentlichung mehr und verwendet nicht portable Build-Automatisierung.

xml-coreutils scheint am besten dokumentiert und am meisten UNIX-orientiert zu sein.


1
Könnten Sie nicht ein Wrapper-Skript für das Ruby-Programm erstellen und das Array der Argumente im Skript an hpricot übergeben? In einem PHP-Shell-Skript sollte beispielsweise Folgendes funktionieren: <? Php / path / to / hpricot $ argv?>
Alastairs

25

Zu Joseph Holstens hervorragender Liste füge ich das xpath-Befehlszeilenskript hinzu, das mit der Perl-Bibliothek XML :: XPath geliefert wird. Eine großartige Möglichkeit, Informationen aus XML-Dateien zu extrahieren:

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

3
Dies wird standardmäßig in osx installiert, jedoch ohne -q -eOptionen. Beispiel: xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
Holen Sie

25

Es gibt auch xml2und 2xmlPaar. Es ermöglicht den üblichen Tools zum Bearbeiten von Zeichenfolgen, XML zu verarbeiten.

Beispiel. 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 Es gibt auch html2/ 2html.


@ Joseph Holsten Ja. Es ermöglicht das Hacken mit XML, ohne über XPath-Dinge nachzudenken.
Vi.

Nett! Ich hatte mich auf Tools konzentriert, die kein Zwischenformat verwenden, aber die Idee einer zeilenorientierten High-Fidelity-Darstellung von XML scheint eine großartige Möglichkeit zu sein, weiterhin echtes grep und sed zu verwenden. Hast du es mit Pyxie versucht? Wie vergleicht es? Irgendwelche anderen linienorientierten Darstellungen? Würden Sie dies besser in Betracht ziehen, als nur XML-Zeilenumbrüche durch eine Entität (& # 10;) zu ersetzen? Auf diese Weise können Sie Datensätze mindestens in dieselbe Zeile einfügen. Oh, und könnten Sie Ihren Beitrag so bearbeiten, dass er einen Link zum Projekt enthält?
Joseph Holsten

@ Joseph Holsten Nein, ich denke nicht, dass das Pyxie-Format nützlicher wäre als das XML2-Format. xml2 bietet "vollständigen Pfad" in verschachtelten XML-Elementen, sodass eine zeilenorientiertere Zuordnung und Ersetzung möglich ist. Auch 2xmlkann leicht XML aus Teil (gefiltert) neu xml2ausgegeben.
Vi.

5
+1 Ich kann das nicht genug verbessern ... cat foo.xml | xml2 | grep /bar | 2xml- gibt dir die gleiche Struktur wie das Original, aber alle Elemente außer "Balken" -Elementen wurden entfernt. Genial.
Mogsie

14

Sie können xmllint verwenden:

xmllint --xpath //title books.xml

Sollte mit den meisten Distributionen gebündelt werden und ist auch mit Cygwin gebündelt.

$ xmllint --version
xmllint: using libxml version 20900

Sehen:

$ 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
Es gibt kein --xpathArgument für xmllint: manpagez.com/man/1/xmllint
Miserable Variable

1
@MiserableVariable: Die Manpage ist falsch. Ich habe mir gerade die Manpage für meine Version angesehen: Das Argument xpath ist nicht aufgeführt. Dies ist ein Dokumentationsfehler. Versuchen Sie stattdessen, das Programm auszuführen.
Dave Jarvis

2
@MiserableVariable --xpathist eine relativ neue Ergänzung und zB nicht in RHEL 6-Versionen von xmllint.
Daniel Beck

2
xmllint --xpathGenauer gesagt wurde es in libxml2 2.7.7 (2010) eingeführt.
März

9

Wenn Sie nach einer Lösung unter Windows suchen, verfügt Powershell über integrierte Funktionen zum Lesen und Schreiben von XML.

test.xml:

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

Powershell-Skript:

# 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>

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


kämpfte einige Stunden mit verschiedenen Linux-Tools, bevor er auf Powershell zurückgriff. Ich bin überrascht, dass dies so schwer ist - Linux Cmd-Line ist normalerweise sehr gut, aber hier scheint es ein Loch zu geben. Hinweis: Der Anwendungsfall für mich war: 1) Knoten nach xpath suchen, 2) entfernen, falls gefunden, 3) neue Knoten hinzufügen, 4) Datei speichern. Ich habe ein paar Solr-Konfigurationen aktualisiert. Wenn jemand einen einfachen / zuverlässigen Weg kennt, bin ich ganz Ohr
Richard Hauer

Wow, das geht wirklich auf die Zehenspitzen einer akzeptablen Lösung. Aber ehrlich gesagt würde ich es wahrscheinlich akzeptieren, wenn es so aussieht xps $doc .root.one xps $doc 'AppendChild("three")'und xps $doc '.three.set_InnerText("And don't you forget it!")'was eindeutig minderwertig ist!
Joseph Holsten


6

Kommt genau darauf an, was du machen willst.

XSLT ist vielleicht der richtige Weg, aber es gibt eine Lernkurve. Probieren Sie xsltproc aus und beachten Sie, dass Sie Parameter eingeben können.


4

Es gibt auch saxon-lintüber die Befehlszeile die Möglichkeit, XPath 3.0 / XQuery 3.0 zu verwenden. (Andere Befehlszeilentools verwenden XPath 1.0).

BEISPIELE:

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 könnte eine gute Lösung sein. Es ist (relativ) leicht zu erlernen und ein W3C-Standard.

Ich würde XQSharp für einen Befehlszeilenprozessor empfehlen .


1
BaseX verfügt (zusätzlich zum Datenbankmodus) auch über einen Befehlszeilen-XQuery-Prozessor und ist immer auf dem neuesten Stand der Standardversionen (nach dem sich weiterentwickelnden Entwurf von XQuery 3.0).
Charles Duffy


1

Grep-Äquivalent

Sie können eine Bash-Funktion definieren, z. B. "xp" ("xpath"), die Python3-Code umschließt. Um es zu verwenden, müssen Sie python3 und python-lxml installieren. Leistungen:

  1. Regex-Matching, das Ihnen zB in xmllint fehlt.
  2. Verwendung als Filter (in einer Pipe) in der Befehlszeile

Es ist einfach und leistungsstark wie folgt zu bedienen:

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 () sieht ungefähr so ​​aus:

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 Äquivalent

Erwägen Sie die Verwendung von xq, wodurch Sie die volle Leistung der jq "Programmiersprache" erhalten. Wenn Sie Python-Pip installiert haben, können Sie xq mit pip install yq installieren. Im folgenden Beispiel ersetzen wir "Konten behalten" durch "Konten behalten 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 verfügt über ein Plugin namens "XQuery", das Abfragefunktionen für XML-Dokumente bietet.

Nicht ganz die Kommandozeile, aber es funktioniert!


Während JEdit wahrscheinlich eine Möglichkeit hat, eine Datei zu durchsuchen, ist dies kein Konkurrent grep(1).
Joseph Holsten
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.