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

Vorrei 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 dall'alto la versione '1.0.74-SNAPSHOT'?

Mi piacerebbe essere in grado di farlo utilizzando semplici bash scripting sed o awk. Altrimenti si preferisce un semplice pitone.

EDIT

  1. Costrizione

    La casella di linux è in un ambiente aziendale, quindi posso solo utilizzare strumenti già installati (non che non posso richiedere utilità come xml2, ma devo passare attraverso un sacco di nastri red-tape). Alcune delle soluzioni sono molto buone (impara già alcuni nuovi trucchi), ma potrebbero non essere applicabili a causa dell'ambiente limitato

  2. elenchi aggiornati xml

    Ho aggiunto il tag di dependencies all'elenco originale. Ciò mostrerà che una soluzione hacky potrebbe non funzionare in questo caso

  3. distro

    La distro che sto usando è RHEL4

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

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

Altrimenti: xmlgrep e XPath:

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

Svantaggio: lento

Modo Clojure. Richiede solo jvm con speciale file jar:

 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 modo:

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

Ecco un'alternativa a Perl

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

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

Modo Hacky :

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

Sostiene l'inserimento corretto della <version>

Uso di 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 

Utilizzo di 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 

Utilizzo di 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 

Esegui una soluzione molto goffa, una linea

 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" 

Il sed in fondo è molto brutto ma non è stato in grado di printingre il text del nodo solo con mindom.

Aggiornamento da _Vi :

Meno versione hacky 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']" 

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 "Ci sono un sacco di tag di versione nel xml" allora devi dimenticarti di farlo con "strumenti semplici" e regexps, che non lo faranno.

prova questo pitone (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 

Ecco un liner usando sed:

 sed '/<dependencies>/,/<\/dependencies>/d;/<version>/!d;s/ *<\/\?version> *//g' pom.xml 
 Return_text_val=$(xmllint --xpath "//*[local-name()='$TagElmnt']" $FILE ) 

Ecco, prova questo:

 $TagElmnt - TagName $FILE - xml file to parse 
 sed -n "/<name>project-parent/{n;s/.*>\(.*\)<.*/\1/p;q}" pom.xml 

L'opzione -n evita di printingre linee non corrispondenti; la prima partita ( /.../ ) è sulla row prima di quella con il text desiderato; il command n salta alla row successiva, where estrae informazioni rilevanti attraverso un gruppo di cattura ( \(...\) ) e un backreference ( \1 ). p printing fuori, q chiude.

So che la tua domanda dice Linux ma se hai bisogno di farlo su Windows senza bisogno di strumenti di terze parti in modo che tu possa metterlo in un file batch, Powershell può estrarre qualsiasi nodo dal tuo file pom.xml così :

 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