Сценарій: що найпростіше витягнути значення з тегу XML-файлу?


14

Я хочу прочитати pom.xml ("Проектна модель об'єкта" Maven) і витягнути інформацію про версію. Ось приклад:

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

Як я можу витягнути версію '1.0.74-SNAPSHOT' зверху?

Дуже хотілося б, щоб можна було це зробити, використовуючи прості баш сценарії sed або awk. В іншому випадку кращим є простий пітон.

EDIT

  1. Обмеження

    Ящик linux знаходиться у корпоративному середовищі, тому я можу використовувати лише інструменти, які вже встановлені (не те, що я не можу вимагати утиліти, наприклад xml2, але мені доведеться пройти багато червоних стрічок). Деякі рішення дуже хороші (вже вивчіть кілька нових хитрощів), але вони можуть бути непридатними через обмежене середовище

  2. оновлений список XML

    Я додав тег залежностей до початкового списку. Це покаже, що в цьому випадку певне рішення може не спрацювати

  3. Distro

    Дистрибутив, який я використовую, це RHEL4


Чи достатньо цього stackoverflow.com/questions/29004/… ?
bbaja42

Не зовсім. У xml багато тегів версій (наприклад, під тегом залежності). Я хочу лише "/ проект / версія"
Ентоні Конг

Які інструменти та бібліотеки, пов’язані з xml, доступні? Чи добре в порядку jvm-рішення?
Ві.

Поки я можу сказати, що xml2, xmlgrep та perl XML модуль відсутні. Більшість утилітів командного рядка unix присутні. Районом є Redhat EL 4.
Ентоні Конг

(Я не міг би додати коментар , так що я повинен відповісти у відповідь, надлишкова кілька) Деякі великі відповіді можна знайти тут ..... stackoverflow.com/questions/2735548 / ...
JStrahl

Відповіді:


17

xml2 може конвертувати xml у / з орієнтованого на рядки формату:

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

6

Інший спосіб: xmlgrep та XPath:

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

Недолік: повільний


команда оновлена ​​доxml_grep
GAD3R

6

Використання 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

Використання 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

Використання 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()при використанні xmllintтакож працює!
кев

5

Clojure шлях. Потрібен лише jvm зі спеціальним файлом 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))"

Шкала масштабу:

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 спосіб:

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

Це круто! Чудова ідея!
Ентоні Конг

4

Ось альтернатива в Perl

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

Він працює з переглянутим / розширеним прикладом у питаннях, які мають кілька "версій" елементів на різній глибині.


Повільний, (хоча швидший, ніж xmlgrep)
Vi.

3

Хекі- шлях:

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

Покладається на правильне відступ необхідного <version>


Дякую за пропозицію, але, на жаль, це не поверне те, що я хочу. Перегляньте оновлену модель пом.
Ентоні Конг

Повертає "1.0.74-SNAPSHOT". Зауважте, що я змінив сценарій, прочитавши про багато <version>речей.
Ві.

Примітка: це рішення надається "просто для розваги" і не призначене для використання у фактичному продукті. Краще використовувати xml2 / xmlgrep / XML :: Просте рішення.
Ві.

Спасибі! незважаючи на те, що це "просто для розваги", але це, мабуть, "найбільш підходяще" рішення на сьогоднішній день, оскільки воно має мінімальну кількість залежностей: вимагає лише perl ;-)
Ентоні Конг

Що робити з Java? Використання файлів pom передбачає встановлення JVM.
Ві.

3

Розробіть дуже незграбне однолінійне рішення

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"

Сед в кінці дуже некрасивий, але я не зміг роздрукувати текст вузла лише з розумом.

Оновлення від _Vi :

Менш вибаглива версія 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()"

Оновлення від мене

Інша версія:

    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

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

Якщо xsltproc є у вашій системі, і він, ймовірно, такий, як libxslt є на RHEL4, ви можете використовувати його та вищевказаний таблицю стилів для виведення тегу, тобто xsltproc x.xsl prom.xsl.
fpmurphy

2

якщо "У темі xml багато тегів версій", тоді вам краще забути про це з "простими інструментами" та регулярними виразами, це не зробить.

спробуйте цей пітон (немає залежностей):

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

Що саме робить цей сценарій?
Саймон Шихан

він завантажує XML як структуру DOM, використовуючи minidom реалізації Python: docs.python.org/library/xml.dom.minidom.html ідея полягає в тому, щоб схопити унікальний тег <project>, а потім повторити його над дочірніми вузлами (прямі лише для дітей), щоб знайти тег <version>, який ми шукаємо, а не інші теги з такою ж назвою в інших місцях.
Самусь_

1

Ось одноколісний за допомогою sed:

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

1
Покладається на відсутність параметрів у елементах, а додаткові <version>s можуть бути лише всередині залежностей.
Ві.

1

awk працює чудово, не використовуючи зайвих інструментів.
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>

простий і розбірливий спосіб отримати значення <packaging>тегу:

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

1
Це, здається, працює, але будьте обережні: що він робить, це встановити роздільник поля (FS) набір символів <і>; то він знаходить усі рядки зі словом "упаковка" в них і дає вам третє поле.
SMerrill8

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

Ось спробуйте:

$TagElmnt - TagName
$FILE - xml file to parse

0

Я знаю, що ваше питання говорить про Linux, але якщо у вас є необхідність робити це в Windows без необхідності будь-яких сторонніх інструментів, щоб ви могли помістити його в пакетний файл, Powershell може витягнути будь-який вузол з вашого файлу pom.xml, як так :

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 тепер є відкритим кодом і працює на Linux та інших платформах. Ми використовуємо його для побудови, віддаючи перевагу bash, cygwin та ming64.
Charlweed

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

Ця -nопція дозволяє уникнути друку невідповідних рядків; перша відповідність ( /.../) знаходиться на рядку перед тим, у кого потрібний текст; nкоманда переходить до наступного рядка, де sекстракти відповідної інформації через групу в захоплення ( \(...\)) і зворотну посилання ( \1). pроздруковує, qзакриває.


2
Чи можете ви розширити свою відповідь, щоб пояснити це? Спасибі.
fixer1234
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.