Skrypty: co jest najłatwiejsze do wyodrębnienia wartości w znaczniku pliku XML?


14

Chcę przeczytać plik pom.xml („Project Object Model” Maven) i wyodrębnić informacje o wersji. Oto przykład:

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

Jak mogę wyodrębnić wersję „1.0.74-SNAPSHOT” z powyższego?

Chciałbym móc to zrobić za pomocą prostych skryptów bash sed lub awk. W przeciwnym razie preferowany jest prosty python.

EDYTOWAĆ

  1. Przymus

    Linux jest w środowisku korporacyjnym, więc mogę korzystać tylko z narzędzi, które są już zainstalowane (nie dlatego, że nie mogę zażądać narzędzi takich jak xml2, ale muszę przejść wiele biurokracji). Niektóre rozwiązania są bardzo dobre (poznaj już kilka nowych sztuczek), ale mogą nie mieć zastosowania ze względu na ograniczone środowisko

  2. zaktualizowana lista xml

    Dodałem tag zależności do pierwotnego wpisu. To pokaże, że niektóre zhackowane rozwiązania mogą nie działać w tym przypadku

  3. Distro

    Distro, którego używam, to RHEL4



Nie całkiem. W pliku XML znajduje się wiele znaczników wersji (np. Znacznik zależności). Chcę tylko „/ project / version”
Anthony Kong,

Jakie narzędzia i biblioteki związane z XML są dostępne? Czy rozwiązania oparte na jvm są w porządku?
Vi.

Do tej pory mogę powiedzieć, że moduł XML xml2, xmlgrep i perl nie są obecne. Obecnych jest większość narzędzi wiersza poleceń unix. Distro to Redhat EL 4.
Anthony Kong,

(Nie mogłem dodać komentarza, więc muszę odpowiedzieć jako odpowiedź, nieco przesadzić). Kilka świetnych odpowiedzi można znaleźć tutaj ..... stackoverflow.com/questions/2735548/
JStrahl

Odpowiedzi:


17

xml2 może konwertować xml do / z formatu liniowego:

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

6

Inny sposób: xmlgrep i XPath:

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

Wada: powolna


zaktualizowano polecenie doxml_grep
GAD3R

6

Za pomocą 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

Za pomocą 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

Za pomocą 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()przy użyciu xmllintdziała również!
kev

5

Sposób Clojure. Wymaga tylko jvm ze specjalnym plikiem 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))"

Sposób 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 })'

Groovy way:

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

To jest niesamowite! Świetny pomysł!
Anthony Kong

4

Oto alternatywa w Perlu

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

Działa ze zmienionym / rozszerzonym przykładem w pytaniach, które mają wiele elementów „wersji” na różnych głębokościach.


Powolny (choć szybszy niż xmlgrep)
Vi.

3

Hacky sposób:

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

Opiera się na prawidłowym wcięciu wymaganego <version>


Dzięki za sugestię, ale niestety nie zwróci tego, czego chcę. Zobacz zaktualizowany model pom.
Anthony Kong,

Zwraca „1.0.74-SNAPSHOT”. Zauważ, że zmieniłem skrypt po przeczytaniu o wielu <version>rzeczach.
Vi.

Uwaga: to rozwiązanie jest dostarczane „tylko dla zabawy” i nie jest przeznaczone do użycia w rzeczywistym produkcie. Lepiej użyj xml2 / xmlgrep / XML :: Proste rozwiązanie.
Vi.

Dzięki! mimo że jest to „tylko dla zabawy”, ale zdecydowanie jest to zdecydowanie „najbardziej odpowiednie” rozwiązanie, ponieważ ma minimalną liczbę zależności: Wymaga tylko perla ;-)
Anthony Kong

Co z robieniem tego z Javy? Korzystanie z plików pom wymaga zainstalowania JVM.
Vi.

3

Opracuj bardzo niezdarne, jedno-liniowe rozwiązanie

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"

Sed na końcu jest bardzo brzydki, ale nie byłem w stanie wydrukować tekstu węzła sam z Mindom.

Aktualizacja od _Vi :

Mniej zhackowana wersja Pythona:

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

Zaktualizuj ode mnie

Inna wersja:

    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

Sposób 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

Jeśli xsltproc jest w twoim systemie i prawdopodobnie jest tak, jak libxslt na RHEL4, możesz użyć go i powyższego arkusza stylów do wyprowadzenia tagu, tj. Xsltproc x.xsl prom.xsl.
fpmurphy

2

jeśli „w pliku XML jest dużo znaczników wersji”, lepiej zapomnij o zrobieniu tego za pomocą „prostych narzędzi” i wyrażeń regularnych, to nie wystarczy.

wypróbuj ten python (bez zależności):

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

Co dokładnie robi ten skrypt?
Simon Sheehan,

ładuje XML jako strukturę DOM za pomocą implementacji minidom Pythona: docs.python.org/library/xml.dom.minidom.html chodzi o to, aby pobrać unikalny tag <project>, a następnie iterować jego węzły potomne (bezpośrednio tylko childs), aby znaleźć tag <wersja>, którego szukamy, a nie inne tagi o tej samej nazwie w innych miejscach.
Samus_

1

Oto jedna linijka za pomocą sed:

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

1
Opiera się na braku parametrów w elementach, a dodatkowe <version>s mogą być tylko wewnątrz zależności.
Vi.

1

awk działa dobrze bez użycia dodatkowych narzędzi.
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>

prosty i czytelny sposób na uzyskanie wartości <packaging>tagu:

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

1
Wydaje się, że to działa, ale uwaga: ustawia separator pól (FS) na zestaw znaków <i>; następnie znajduje wszystkie wiersze ze słowem „pakowanie” i podaje trzecie pole.
SMerrill8

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

Tutaj spróbuj tego:

$TagElmnt - TagName
$FILE - xml file to parse

0

Wiem, że twoje pytanie mówi o Linuksie, ale jeśli musisz to zrobić w systemie Windows bez użycia narzędzi innych firm, takich, które można umieścić w pliku wsadowym, Powershell może wyodrębnić dowolny węzeł z pliku pom.xml w ten sposób :

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 jest teraz open source i działa na Linuksie i innych platformach. Używamy go do budowania zamiast bash, cygwin i ming64.
Charlweed

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

Ta -nopcja pozwala uniknąć drukowania niepasujących linii; pierwsze dopasowanie ( /.../) jest w linii przed tą z poszukiwanym tekstem; nrozkaz przechodzi do następnej linii, gdzie sekstrakty istotnych informacji thru grupy przechwytujące ( \(...\)) i wsteczne ( \1). pdrukuje, qwychodzi.


2
Czy możesz rozszerzyć swoją odpowiedź, aby to wyjaśnić? Dzięki.
fixer1234
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.