Scripting: Was ist am einfachsten, um einen Wert in einem Tag einer XML-Datei zu extrahieren?


14

Ich möchte eine pom.xml ('Project Object Model' von Maven) lesen und die Versionsinformationen extrahieren. Hier ist ein Beispiel:

<?xml version="1.0" encoding="UTF-8"?><project 
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>project-parent</artifactId>
    <name>project-parent</name>
    <version>1.0.74-SNAPSHOT</version>
    <dependencies>
        <dependency>
        <groupId>com.sybase.jconnect</groupId>
        <artifactId>jconnect</artifactId>
        <version>6.05-26023</version>
    </dependency>
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>1.5.2</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jdmk</groupId>
        <artifactId>jmxtools</artifactId>
        <version>1.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.easymock</groupId>
        <artifactId>easymock</artifactId>
        <version>2.4</version>
    </dependency>       
</dependencies>
</project>

Wie kann ich die Version '1.0.74-SNAPSHOT' von oben extrahieren?

Würde gerne in der Lage sein, dies mit einfachen Bash-Skripten sed oder awk zu tun. Ansonsten wird eine einfache Python bevorzugt.

BEARBEITEN

  1. Zwang

    Die Linux-Box befindet sich in einer Unternehmensumgebung, sodass ich nur bereits installierte Tools verwenden kann (nicht, dass ich kein Hilfsprogramm wie xml2 anfordern kann, aber viel Bürokratie aufwenden muss). Einige der Lösungen sind sehr gut (lernen Sie bereits ein paar neue Tricks), können jedoch aufgrund der eingeschränkten Umgebung nicht angewendet werden

  2. aktualisierte XML-Liste

    Ich habe das Dependencies-Tag zum ursprünglichen Listing hinzugefügt. Dies zeigt, dass in diesem Fall eine hackige Lösung möglicherweise nicht funktioniert

  3. Distro

    Die Distribution, die ich benutze, ist RHEL4



Nicht wirklich. Es gibt viele Versions-Tags in der XML (z. B. unter dem Tag "Abhängigkeiten"). Ich möchte nur "/ project / version"
Anthony Kong

Welche XML-bezogenen Tools und Bibliotheken sind verfügbar? Sind jvm-basierte Lösungen in Ordnung?
Vi.

Bisher kann ich sagen, xml2, xmlgrep und Perl XML-Modul sind nicht vorhanden. Die meisten Unix-Befehlszeilendienstprogramme sind vorhanden. Die Distribution ist Redhat EL 4.
Anthony Kong

(Ich konnte keinen Kommentar hinzufügen, daher muss ich antworten, etwas übertrieben) Einige großartige Antworten finden Sie hier ..... stackoverflow.com/questions/2735548/…
JStrahl

Antworten:


17

xml2 kann xml in ein zeilenorientiertes Format konvertieren:

xml2 < pom.xml  | grep /project/version= | sed 's/.*=//'

6

Anderer Weg: xmlgrep und XPath:

xmlgrep --text_only '/project/version' pom.xml

Nachteil: langsam


Befehl aktualisiert aufxml_grep
GAD3R

6

Verwenden python

$ python -c 'from xml.etree.ElementTree import ElementTree; print ElementTree(file="pom.xml").findtext("{http://maven.apache.org/POM/4.0.0}version")'
1.0.74-SNAPSHOT

Verwenden xmlstarlet

$ xml sel -N x="http://maven.apache.org/POM/4.0.0" -t -m 'x:project/x:version' -v . pom.xml
1.0.74-SNAPSHOT

Verwenden xmllint

$ echo -e 'setns x=http://maven.apache.org/POM/4.0.0\ncat /x:project/x:version/text()' | xmllint --shell pom.xml | grep -v /
1.0.74-SNAPSHOT

cat (//x:version)[1]/text()bei der verwendung klappt das xmllintauch!
Kev

5

Clojure Weg. Benötigt nur JVM mit spezieller JAR-Datei:

java -cp clojure.jar clojure.main -e "(use 'clojure.xml) (->> (java.io.File. \"pom.xml\") (clojure.xml/parse) (:content) (filter #(= (:tag %) :version)) (first) (:content) (first) (println))"

Scala Weg:

java -Xbootclasspath/a:scala-library.jar -cp scala-compiler.jar scala.tools.nsc.MainGenericRunner -e 'import scala.xml._; println((XML.load(new java.io.FileInputStream("pom.xml")) match { case <project>{children @ _*}</project> => for (i <- children if (i  match { case <version>{children @ _*}</version> => true; case _ => false;  }))  yield i })(0) match { case <version>{Text(x)}</version> => x })'

Grooviger Weg:

java -classpath groovy-all.jar groovy.ui.GroovyMain -e 'println (new XmlParser().parse(new File("pom.xml")).value().findAll({ it.name().getLocalPart()=="version" }).first().value().first())'

Das ist fantastisch! Großartige Idee!
Anthony Kong

4

Hier ist eine Alternative in Perl

$ perl -MXML::Simple -e'print XMLin("pom.xml")->{version}."\n"'
1.0.74-SNAPSHOT

Es funktioniert mit dem überarbeiteten / erweiterten Beispiel in den Fragen, das mehrere "Versions" -Elemente in unterschiedlichen Tiefen enthält.


Langsam (obwohl schneller als xmlgrep)
Vi.

3

Hacky Way:

perl -e '$_ = join "", <>; m!<project[^>]*>.*\n(?:    |\t)<version[^>]*>\s*([^<]+?)\s*</version>.*</project>!s and print "$1\n"' pom.xml

Setzt korrekte Einrückung voraus <version>


Danke für den Vorschlag, aber leider wird nicht zurückgegeben, was ich will. Bitte beachten Sie das aktualisierte POM-Modell.
Anthony Kong

Gibt "1.0.74-SNAPSHOT" zurück. Beachten Sie, dass ich das Skript geändert habe, nachdem ich mehrere <version>Dinge gelesen habe .
Vi.

Hinweis: Diese Lösung wird "nur zum Spaß" bereitgestellt und ist nicht für die Verwendung in tatsächlichen Produkten vorgesehen. Verwenden Sie besser xml2 / xmlgrep / XML :: Simple.
Vi.

Vielen Dank! Obwohl es "nur zum Spaß" ist, ist es wahrscheinlich die bei weitem am besten geeignete Lösung, da es nur eine minimale Anzahl von Abhängigkeiten aufweist: Es wird nur Perl benötigt ;-)
Anthony Kong

Was ist mit Java? Die Verwendung von pom-Dateien setzt voraus, dass JVM installiert ist.
Vi.

3

Erarbeiten Sie eine sehr ungeschickte Ein-Liner-Lösung

python -c "from xml.dom.minidom import parse;dom = parse('pom.xml');print [n for n in dom.getElementsByTagName('version') if n.parentNode == dom.childNodes[0]][0].toxml()" | sed -e "s/.*>\(.*\)<.*/\1/g"

Das Sed am Ende ist sehr hässlich, aber ich konnte den Text des Knotens nicht mit mindom alleine ausdrucken.

Update von _Vi :

Weniger hackige Python-Version:

python -c "from xml.dom.minidom import parse;dom = parse('pom.xml');print [i.childNodes.item(0).nodeValue for i in dom.firstChild.childNodes if i.nodeName == 'version'].pop()"

Update von mir

Andere Version:

    python -c "from  xml.dom.minidom import parse;dom = parse('pom.xml');print [n.firstChild.data for n in dom.childNodes[0].childNodes if n.firstChild and n.tagName == 'version']"

2

XSLT-Weg:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text"/>

        <xsl:template match="/">
                <xsl:for-each select="*[local-name()='project']">
                    <xsl:for-each select="*[local-name()='version']">
                        <xsl:value-of select="text()"/>
                    </xsl:for-each>
                </xsl:for-each>
        </xsl:template>
</xsl:stylesheet>
xalan -xsl x.xsl -in pom.xml

Wenn sich xsltproc auf Ihrem System befindet und es wahrscheinlich so ist wie libxslt auf RHEL4, können Sie es und das obige Stylesheet verwenden, um das Tag auszugeben, dh xsltproc x.xsl prom.xsl.
fpmurphy

2

Wenn "Es gibt viele Versions-Tags in der XML", vergessen Sie besser, dies mit "einfachen Werkzeugen" und regulären Ausdrücken zu tun, das reicht nicht.

Probieren Sie diese Python (keine Abhängigkeiten):

from xml.dom.minidom import parse

dom = parse('pom.xml')
project = dom.getElementsByTagName('project')[0]
for node in project.childNodes:
    if node.nodeType == node.ELEMENT_NODE and node.tagName == 'version':
        print node.firstChild.nodeValue

Was genau macht dieses Skript?
Simon Sheehan

Es lädt die XML-Datei als DOM-Struktur mithilfe der Minidom-Implementierung von Python: docs.python.org/library/xml.dom.minidom.html Die Idee ist, das eindeutige <project> -Tag zu erfassen und dann über die untergeordneten Knoten zu iterieren (direkt) childs only), um das gesuchte Tag <version> zu finden und keine anderen Tags mit demselben Namen an anderen Stellen.
Samus_

1

Hier ist ein Einzeiler mit sed:

sed '/<dependencies>/,/<\/dependencies>/d;/<version>/!d;s/ *<\/\?version> *//g' pom.xml

1
Verlässt sich auf das Fehlen von Parametern in Elementen, und diese zusätzlichen <version>s können nur innerhalb von Abhängigkeiten sein.
Vi.

0
Return_text_val=$(xmllint --xpath "//*[local-name()='$TagElmnt']" $FILE )

Hier, versuchen Sie dies:

$TagElmnt - TagName
$FILE - xml file to parse

0

Ich weiß, dass Ihre Frage Linux lautet, aber wenn Sie dies unter Windows tun müssen, ohne Tools von Drittanbietern zu benötigen, damit Sie es in eine Batch-Datei einfügen können, kann Powershell jeden Knoten wie folgt aus der Datei pom.xml extrahieren :

powershell -Command "& {select-xml //pom:project/pom:properties/pom:mypluginversion -path pom.xml -Namespace  @{pom='http://maven.apache.org/POM/4.0.0'} | foreach {$_.Node.Innerxml}}" > myPluginVersion.txt

Powershell ist jetzt Open Source und läuft unter Linux und anderen Plattformen. Wir verwenden es, um vor Bash, Cygwin und Ming64 zu bauen.
Charlweed

0
sed -n "/<name>project-parent/{n;s/.*>\(.*\)<.*/\1/p;q}" pom.xml

Mit dieser -nOption wird vermieden, dass nicht übereinstimmende Zeilen gedruckt werden. first match ( /.../) steht in der Zeile vor der Zeile mit dem gesuchten Text. Der nBefehl springt zur nächsten Zeile, in der srelevante Informationen durch eine Erfassungsgruppe ( \(...\)) und eine Rückverweisung ( \1) extrahiert werden . pdruckt aus, qbeendet.


2
Können Sie Ihre Antwort erweitern, um dies zu erklären? Vielen Dank.
Fixer1234

0

awk funktioniert ohne zusätzliche Werkzeuge.
cat pod.xml

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.networks.app</groupId>
  <artifactId>operation-platform</artifactId>
  <version>1.0.0</version>
  <packaging>tar.xz</packaging>
  <description>POM was created by Sonatype Nexus</description>
</project>

einfache und lesbare Möglichkeit, den Wert des <packaging>Tags zu ermitteln:

cat pod.xml | awk -F'[<>]' '/packaging/{print $3}'
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.