Scripting: quelle est la méthode la plus simple pour extraire une valeur dans une balise d'un fichier XML?


14

Je veux lire un pom.xml ('Project Object Model' de Maven) et extraire les informations de version. Voici un exemple:

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

Comment puis-je extraire la version '1.0.74-SNAPSHOT' d'en haut?

J'adorerais pouvoir le faire en utilisant un script bash simple sed ou awk. Sinon, un simple python est préféré.

ÉDITER

  1. Contrainte

    La boîte Linux est dans un environnement d'entreprise, donc je ne peux utiliser que des outils déjà installés (pas que je ne puisse pas demander d'utilitaire comme xml2, mais je dois passer par beaucoup de paperasserie). Certaines des solutions sont très bonnes (apprenez déjà quelques nouvelles astuces), mais elles peuvent ne pas être applicables en raison de l'environnement restreint

  2. liste xml mise à jour

    J'ai ajouté la balise de dépendances à la liste d'origine. Cela montrera qu'une solution hacky peut ne pas fonctionner dans ce cas

  3. Distro

    La distribution que j'utilise est RHEL4



Pas vraiment. Il y a beaucoup de balises de version dans le xml (par exemple sous la balise dependencies). Je veux seulement '/ project / version'
Anthony Kong

Quels outils et bibliothèques xml sont disponibles? Les solutions basées sur jvm sont-elles correctes?
Vi.

Jusqu'à présent, je peux dire que xml2, xmlgrep et le module XML perl ne sont pas présents. La plupart des utilitaires de ligne de commande Unix sont présents. La distribution est Redhat EL 4.
Anthony Kong

(Je n'ai pas pu ajouter de commentaire, je dois donc répondre comme une réponse, un peu exagéré) Quelques bonnes réponses peuvent être trouvées ici ..... stackoverflow.com/questions/2735548/…
JStrahl

Réponses:


17

xml2 peut convertir du xml vers / à partir d'un format orienté ligne:

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

6

Autre moyen: xmlgrep et XPath:

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

Inconvénient: lent


commande mise à jour versxml_grep
GAD3R

6

En utilisant 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

En utilisant 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

En utilisant 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()lors de l'utilisation xmllintfonctionne également!
kev

5

Façon clojure. Nécessite uniquement jvm avec un fichier jar spécial:

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

Manière 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 })'

Façon 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())'

C'est génial! Bonne idée!
Anthony Kong

4

Voici une alternative en Perl

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

Il fonctionne avec l'exemple révisé / étendu dans les questions qui a plusieurs éléments "version" à différentes profondeurs.


Lent, (bien que plus rapide que xmlgrep)
Vi.

3

Façon hacky :

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

Repose sur une indentation correcte des éléments requis <version>


Merci pour la suggestion, mais malheureusement elle ne retournera pas ce que je veux. Veuillez consulter le modèle de pom mis à jour.
Anthony Kong

Renvoie "1.0.74-SNAPSHOT". Notez que j'ai changé le script après avoir lu plusieurs <version>choses.
Vi.

Remarque: cette solution est fournie «juste pour le plaisir» et n'est pas destinée à être utilisée dans le produit réel. Mieux utiliser xml2 / xmlgrep / XML :: Solution simple.
Vi.

Merci! même si c'est «juste pour le plaisir» mais c'est probablement la solution «la plus appropriée» de loin car elle a un nombre minimum de dépendances: elle ne nécessite que perl ;-)
Anthony Kong

Qu'en est-il de le faire à partir de Java? L'utilisation de fichiers pom implique l'installation de JVM.
Vi.

3

Élaborez une solution à un revêtement très maladroite

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"

Le sed à la fin est très moche mais je n'ai pas pu imprimer le texte du nœud avec mindom seul.

Mise à jour depuis _Vi :

Version Python moins 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()"

Mise à jour de moi

Une autre 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

Manière 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 sur votre système, et c'est probablement comme libxslt sur RHEL4, alors vous pouvez l'utiliser et la feuille de style ci-dessus pour sortir la balise, c'est-à-dire xsltproc x.xsl prom.xsl.
fpmurphy

2

si "il y a beaucoup de balises de version dans le xml", vous feriez mieux d'oublier de le faire avec des "outils simples" et des expressions rationnelles, cela ne suffira pas.

essayez ce python (pas de dépendances):

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

Que fait exactement ce script?
Simon Sheehan

il charge le XML en tant que structure DOM à l'aide de l'implémentation de minidom de Python: docs.python.org/library/xml.dom.minidom.html l'idée est de saisir la balise <project> qui est unique, puis d'itérer sur ses nœuds enfants (direct enfants uniquement) pour trouver la balise <version> que nous recherchons et non d'autres balises portant le même nom à d'autres endroits.
Samus_

1

Voici un one-liner utilisant sed:

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

1
Dépend de l'absence de paramètres dans les éléments et que les <version>s supplémentaires ne peuvent être que dans les dépendances.
Vi.

1

awk fonctionne très bien sans utiliser d'outils supplémentaires.
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>

moyen simple et lisible pour obtenir la valeur du <packaging>tag:

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

1
Cela semble fonctionner, mais méfiez-vous: ce qu'il fait est de définir le séparateur de champ (FS) sur le jeu de caractères <et>; il trouve ensuite toutes les lignes contenant le mot "packaging" et vous donne le troisième champ.
SMerrill8

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

Ici, essayez ceci:

$TagElmnt - TagName
$FILE - xml file to parse

0

Je sais que votre question dit Linux mais si vous avez besoin de le faire sur Windows sans avoir besoin d'outils tiers tels que vous pouvez le mettre dans un fichier batch, Powershell peut extraire n'importe quel nœud du fichier pom.xml comme ceci :

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 est désormais open source et fonctionne sur Linux et d'autres plateformes. Nous l'utilisons pour construire de préférence à bash, cygwin et ming64.
Charlweed

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

L' -noption évite d'imprimer des lignes qui ne correspondent pas; la première correspondance ( /.../) est sur la ligne avant celle avec le texte voulu; la ncommande passe à la ligne suivante, où sextrait les informations pertinentes via un groupe de capture ( \(...\)) et une référence arrière ( \1). pimprime, qquitte.


2
Pouvez-vous développer votre réponse pour expliquer cela? Merci.
fixer1234
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.