Scripting: ¿cuál es la forma más fácil de extraer un valor en una etiqueta de un archivo XML?


14

Quiero leer un pom.xml ('Modelo de objetos de proyecto' de Maven) y extraer la información de la versión. Aquí hay un ejemplo:

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

¿Cómo puedo extraer la versión '1.0.74-SNAPSHOT' de arriba?

Me encantaría poder hacerlo usando un simple script de bash sed o awk. De lo contrario, se prefiere una pitón simple.

EDITAR

  1. Restricción

    La caja de Linux está en un entorno corporativo, por lo que solo puedo usar herramientas que ya están instaladas (no es que no pueda solicitar una utilidad como xml2, pero tengo que pasar por muchos trámites burocráticos). Algunas de las soluciones son muy buenas (ya aprende algunos trucos nuevos), pero pueden no ser aplicables debido al entorno restringido

  2. listado xml actualizado

    Agregué la etiqueta de dependencias a la lista original. Esto mostrará que alguna solución hacky puede no funcionar en este caso

  3. Distro

    La distribución que estoy usando es RHEL4



Realmente no. Hay muchas etiquetas de versión en el xml (por ejemplo, bajo etiqueta de dependencias). Solo quiero '/ proyecto / versión'
Anthony Kong

¿Qué herramientas y bibliotecas relacionadas con xml están disponibles? ¿Están bien las soluciones basadas en jvm?
Vi.

Hasta ahora puedo decir que xml2, xmlgrep y el módulo XML perl no están presentes. La mayoría de las utilidades de línea de comandos de Unix están presentes. La distribución es Redhat EL 4.
Anthony Kong

(No pude agregar un comentario, así que tengo que responder como una respuesta, exagerar un poco) Algunas respuestas excelentes se pueden encontrar aquí ... stackoverflow.com/questions/2735548/…
JStrahl

Respuestas:


17

xml2 puede convertir xml a / desde formato orientado a línea:

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

6

Otra forma: xmlgrep y XPath:

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

Desventaja: lento


comando actualizado axml_grep
GAD3R

6

Utilizando 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

Utilizando 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

Utilizando 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()cuando se usa xmllinttambién funciona!
kev

5

Clojure camino. Requiere solo jvm con un archivo 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))"

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

Manera maravillosa:

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

¡Esto es asombroso! ¡Gran idea!
Anthony Kong

4

Aquí hay una alternativa en Perl

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

Funciona con el ejemplo revisado / extendido en las preguntas que tiene múltiples elementos de "versión" a diferentes profundidades.


Lento, (aunque más rápido que xmlgrep)
Vi.

3

Hacky way:

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

Se basa en la sangría correcta de la requerida <version>


Gracias por la sugerencia, pero desafortunadamente no devolverá lo que quiero. Por favor vea el modelo de pom actualizado.
Anthony Kong

Devuelve "1.0.74-INSTANTÁNEA". Tenga en cuenta que cambié el guión después de leer sobre varias <version>cosas.
Vi.

Nota: esta solución se proporciona "solo por diversión" y no está diseñada para usarse en el producto real. Mejor uso xml2 / xmlgrep / XML :: Solución simple.
Vi.

¡Gracias! a pesar de que es "solo por diversión", pero es probablemente la solución "más adecuada" con mucho porque tiene un número mínimo de dependencias: solo requiere perl ;-)
Anthony Kong

¿Qué hay de hacerlo desde Java? Usar archivos pom implica tener JVM instalado.
Vi.

3

Encuentre una solución muy torpe y de una sola línea.

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"

El sed al final es muy feo, pero no pude imprimir el texto del nodo solo con Mindom.

Actualización de _Vi :

Versión de 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()"

Actualización de mi parte

Otra 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

Forma 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

Si xsltproc está en su sistema, y ​​probablemente sea igual que libxslt en RHEL4, puede usarlo junto con la hoja de estilo anterior para generar la etiqueta, es decir, xsltproc x.xsl prom.xsl.
fpmurphy

2

si "Hay muchas etiquetas de versión en el xml", entonces es mejor que se olvide de hacerlo con "herramientas simples" y expresiones regulares, eso no funcionará.

prueba este python (sin dependencias):

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

¿Qué hace exactamente este script?
Simon Sheehan

carga el XML como una estructura DOM utilizando la implementación minidom de Python: docs.python.org/library/xml.dom.minidom.html la idea es tomar la etiqueta <project> que es única y luego iterar sobre sus nodos secundarios (directo solo para niños) para encontrar la etiqueta <versión> que estamos buscando y no otras etiquetas con el mismo nombre en otros lugares.
Samus_

1

Aquí hay una línea usando sed:

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

1
Se basa en la ausencia de parámetros en los elementos y que los <version>s adicionales solo pueden estar dentro de las dependencias.
Vi.

1

awk funciona bien sin usar herramientas adicionales.
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>

forma simple y legible de obtener el valor de la <packaging>etiqueta:

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

1
Esto parece funcionar, pero tenga cuidado: lo que hace es establecer el separador de campo (FS) en el conjunto de caracteres <y>; luego encuentra todas las líneas con la palabra "empaque" en ellas y le da el tercer campo.
SMerrill8

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

Aquí, prueba esto:

$TagElmnt - TagName
$FILE - xml file to parse

0

Sé que su pregunta dice Linux, pero si tiene la necesidad de hacer esto en Windows sin la necesidad de herramientas de terceros para que pueda colocarlo en un archivo por lotes, Powershell puede extraer cualquier nodo del archivo pom.xml de esta manera. :

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 ahora es de código abierto y se ejecuta en Linux y otras plataformas. Lo usamos para construir con preferencia a bash, cygwin y ming64.
Charlweed

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

La -nopción evita imprimir líneas que no coinciden; first match ( /.../) está en la línea antes de la que tiene el texto deseado; el ncomando salta a la siguiente línea, donde sextrae información relevante a través de un grupo de captura ( \(...\)) y una referencia inversa ( \1). pimprime, se qcierra.


2
¿Puedes ampliar tu respuesta para explicar esto? Gracias.
fijador1234
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.