Scripting: qual é a maneira mais fácil de extrair um valor em uma tag de um arquivo XML?

12

Eu quero ler um pom.xml ('Project Object Model' do Maven) e extrair as informações da versão. Aqui está um exemplo:

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

Como posso extrair a versão '1.0.74-SNAPSHOT' de cima?

Adoraria poder fazer isso usando o simples script bash sed ou awk. Caso contrário, um simples python é o preferido.

EDITAR

  1. Restrição

    A caixa linux está em um ambiente corporativo, então eu só posso usar ferramentas que já estão instaladas (não que eu não possa solicitar utilitários como o xml2, mas eu tenho que passar por muita burocracia). Algumas das soluções são muito boas (aprenda alguns truques novos), mas elas podem não ser aplicáveis devido ao ambiente restrito

  2. lista xml atualizada

    Eu adicionei a tag de dependências à listagem original. Isso mostrará que alguma solução hacky pode não funcionar neste caso

  3. Distro

    A distro que estou usando é RHEL4

por Anthony Kong 20.12.2011 / 23:01

13 respostas

16

xml2 pode converter xml para / de formato orientado a linhas:

xml2 < pom.xml  | grep /project/version= | sed 's/.*=//'
    
por 20.12.2011 / 23:21
6

Usando 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

Usando 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

Usando 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
    
por 21.12.2011 / 05:54
5

Outra maneira: xmlgrep e XPath:

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

Desvantagem: lenta

    
por 20.12.2011 / 23:43
5

Caminho de Clojure. Requer apenas jvm com arquivo jar especial:

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

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

Caminho 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())'
    
por 21.12.2011 / 01:00
4

Aqui está uma alternativa em Perl

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

Funciona com o exemplo revisado / estendido nas questões que têm vários elementos de "versão" em diferentes profundidades.

    
por 20.12.2011 / 23:45
3

Hacky da maneira:

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

Baseia-se no recuo correto do <version> necessário

    
por 20.12.2011 / 23:55
3

Desenvolva uma solução muito desajeitada e de uma única linha

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/.*>\(.*\)<.*//g"

O sed no final é muito feio, mas eu não consegui imprimir o texto do nó com o mindom sozinho.

Atualizar de _Vi :

Versão Python menos hacky:

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

Atualização minha

Outra versão:

    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']"
    
por 21.12.2011 / 00:24
2

Caminho 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
    
por 21.12.2011 / 02:16
2

se "Há muita tag de versão no xml", é melhor você esquecer de fazer isso com "simple tools" e regexps, isso não serve.

experimente este python (sem dependências):

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
    
por 22.12.2011 / 02:38
1

Aqui está um one-liner usando sed:

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

Aqui, tente isto:

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

A opção -n evita imprimir linhas não correspondentes; a primeira correspondência ( /.../ ) está na linha antes da que tem o texto desejado; o comando n pula para a próxima linha, onde s extrai informações relevantes através de um grupo de captura ( \(...\) ) e uma referência anterior ( ). p imprime, q desiste.

    
por 27.10.2015 / 00:04
-1

Sei que sua pergunta diz Linux, mas se você precisa fazer isso no Windows sem a necessidade de ferramentas de terceiros para poder colocá-lo em um arquivo de lote, o Powershell pode extrair qualquer nó do seu pom.xml arquivo assim:

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
    
por 26.10.2015 / 22:55