Scripting: qual è il modo più semplice per estrarre un valore in un tag di un file XML?


14

Voglio leggere un pom.xml ("Project Object Model" di Maven) ed estrarre le informazioni sulla versione. Ecco un esempio:

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

Come posso estrarre la versione '1.0.74-SNAPSHOT' dall'alto?

Mi piacerebbe poterlo fare usando bash scripting sed o awk. Altrimenti è preferito un semplice pitone.

MODIFICARE

  1. costrizione

    La scatola di Linux si trova in un ambiente aziendale, quindi posso usare solo strumenti già installati (non che non posso richiedere utility come xml2, ma devo fare molta burocrazia). Alcune delle soluzioni sono molto valide (impara già alcuni nuovi trucchi), ma potrebbero non essere applicabili a causa dell'ambiente limitato

  2. elenco xml aggiornato

    Ho aggiunto il tag delle dipendenze all'elenco originale. Questo mostrerà che una soluzione hacky potrebbe non funzionare in questo caso

  3. distro

    La distro che sto usando è RHEL4



Non proprio. Ci sono molti tag di versione in xml (ad es. Sotto tag di dipendenze). Voglio solo "/ progetto / versione"
Anthony Kong,

Quali strumenti e librerie relativi a XML sono disponibili? Le soltuioni basate su jvm sono OK?
Vi.

Finora posso dire che i moduli XML xml2, xmlgrep e perl non sono presenti. Sono presenti la maggior parte delle utility della riga di comando unix. La distro è Redhat EL 4.
Anthony Kong,

(Non ho potuto aggiungere un commento in modo da devo rispondere come risposta, un po 'eccessivo) Alcuni grandi risposte possono essere trovate qui ..... stackoverflow.com/questions/2735548/...
JStrahl

Risposte:


17

xml2 può convertire xml in / dal formato orientato alla linea:

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

6

Altro modo: xmlgrep e XPath:

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

Svantaggio: lento


comando aggiornato axml_grep
GAD3R

6

utilizzando 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

utilizzando 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

utilizzando 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()quando si utilizza xmllintfunziona anche!
kev

5

Modo Clojure. Richiede solo jvm con file jar speciale:

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:

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 })'

Modo Groovy:

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())'

Questo e spettacolare! Grande idea!
Anthony Kong,

4

Ecco un'alternativa in Perl

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

Funziona con l'esempio rivisto / esteso nelle domande che ha più elementi "versione" a diverse profondità.


Lento (anche se più veloce di xmlgrep)
Vi.

3

Modo Hacky :

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

Si basa sul corretto rientro del necessario <version>


Grazie per il suggerimento, ma sfortunatamente non restituirà ciò che voglio. Si prega di consultare il modello pom aggiornato.
Anthony Kong,

Restituisce "1.0.74-SNAPSHOT". Nota che ho cambiato la sceneggiatura dopo aver letto più <version>cose.
Vi.

Nota: questa soluzione è fornita "solo per divertimento" e non è destinata all'uso nel prodotto reale. Meglio usare xml2 / xmlgrep / XML :: Soluzione semplice.
Vi.

Grazie! anche se è "solo per divertimento" ma è probabilmente la soluzione "più adatta" di gran lunga perché ha un numero minimo di dipendenze: richiede solo perl ;-)
Anthony Kong

Che ne dici di farlo da Java? L'uso dei file pom implica l'installazione di JVM.
Vi.

3

Elaborare una soluzione molto goffa, con una sola riga

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"

La sed alla fine è molto brutta ma non sono stato in grado di stampare il testo del nodo solo con mindom.

Aggiornamento da _Vi :

Versione meno confusa di Python:

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()"

Aggiorna da me

Un'altra versione:

    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

Modo XSLT:

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

Se xsltproc è sul tuo sistema, e probabilmente è come su libxslt su RHEL4, puoi usarlo e il foglio di stile sopra per produrre il tag, cioè xsltproc x.xsl prom.xsl.
fpmurphy il

2

se "Ci sono molti tag versione in xml", allora è meglio dimenticarsi di farlo con "strumenti semplici" e regexps, questo non lo farà.

prova questo python (nessuna dipendenza):

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

Cosa fa esattamente questo script?
Simon Sheehan,

carica l'XML come struttura DOM usando l'implementazione minidom di Python: docs.python.org/library/xml.dom.minidom.html l'idea è quella di afferrare il tag <project> che è unico e quindi iterare sui suoi nodi figlio (diretto solo bambini) per trovare il tag <versione> che stiamo cercando e non altri tag con lo stesso nome in altri luoghi.
Samus_

1

Ecco un one-liner che utilizza sed:

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

1
Si basa sull'assenza di parametri negli elementi e che gli extra <version>possono essere solo all'interno delle dipendenze.
Vi.

1

awk funziona bene senza usare strumenti extra.
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>

modo semplice e leggibile per ottenere il valore del <packaging>tag:

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

1
Questo sembra funzionare, ma attenzione: ciò che fa è impostare il separatore di campo (FS) sull'insieme di caratteri <e>; quindi trova tutte le righe con la parola "packaging" in esse e ti dà il terzo campo.
SMerrill8,

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

Ecco, prova questo:

$TagElmnt - TagName
$FILE - xml file to parse

0

So che la tua domanda dice Linux ma se hai la necessità di farlo su Windows senza la necessità di strumenti di terze parti in modo tale da poterlo mettere in un file batch, Powershell può estrarre qualsiasi nodo dal tuo file pom.xml in questo modo :

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 è ora open source e funziona su Linux e altre piattaforme. Lo usiamo per costruire preferendo bash, cygwin e ming64.
Charlweed,

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

L' -nopzione evita di stampare linee non corrispondenti; la prima corrispondenza ( /.../) è sulla riga prima di quella con il testo desiderato; il ncomando salta alla riga successiva, dove sestrae le informazioni rilevanti attraverso un gruppo di acquisizione ( \(...\)) e un backreference ( \1). pstampa, qesce.


2
Puoi espandere la tua risposta per spiegarlo? Grazie.
Fixer 1234
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.