Viết kịch bản: cách dễ nhất để trích xuất một giá trị trong thẻ của tệp XML là gì?


14

Tôi muốn đọc một tệp pom.xml ('Mô hình đối tượng dự án' của Maven) và trích xuất thông tin phiên bản. Đây là một ví dụ:

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

Làm cách nào tôi có thể trích xuất phiên bản '1.0.74-SNAPSHOT' từ trên?

Rất thích có thể làm như vậy bằng cách sử dụng bash scripting đơn giản hoặc awk. Nếu không, một con trăn đơn giản được ưa thích.

BIÊN TẬP

  1. Hạn chế

    Hộp linux nằm trong môi trường doanh nghiệp nên tôi chỉ có thể sử dụng các công cụ đã được cài đặt (không phải là tôi không thể yêu cầu tiện ích như xml2, nhưng tôi phải trải qua rất nhiều băng đỏ). Một số giải pháp rất tốt (đã học một vài thủ thuật mới), nhưng chúng có thể không được áp dụng do môi trường bị hạn chế

  2. cập nhật danh sách xml

    Tôi đã thêm thẻ phụ thuộc vào danh sách ban đầu. Điều này sẽ cho thấy một số giải pháp hacky có thể không hoạt động trong trường hợp này

  3. Xa

    Bản phân phối tôi đang sử dụng là RHEL4


Liệu stackoverflow.com/questions/29004/ này có đủ không?
bbaja42

Không hẳn vậy. Có rất nhiều thẻ phiên bản trong xml (ví dụ: dưới thẻ phụ thuộc). Tôi chỉ muốn '/ dự án / phiên bản'
Anthony Kong

Những công cụ và thư viện liên quan đến xml có sẵn? Các giải pháp dựa trên jvm có ổn không?
Vi.

Cho đến nay tôi có thể nói rằng mô đun XML xml2, xmlgrep và perl không có mặt. Hầu hết các tiện ích dòng lệnh unix đều có mặt. Bản phân phối là Redhat EL 4.
Anthony Kong

(Tôi không thể thêm một lời nhận xét vì vậy tôi phải trả lời như một câu trả lời, quá mức cần thiết một chút) Một số câu trả lời tuyệt vời có thể được tìm thấy ở đây ..... stackoverflow.com/questions/2735548/...
JStrahl

Câu trả lời:


17

xml2 có thể chuyển đổi xml sang / từ định dạng hướng dòng:

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

6

Cách khác: xmlgrep và XPath:

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

Nhược điểm: chậm


lệnh được cập nhật thànhxml_grep
GAD3R

6

Sử dụng 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

Sử dụng 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

Sử dụng 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()khi sử dụng xmllintcũng có tác dụng!
kev

5

Cách Clojure. Chỉ yêu cầu jvm với tệp jar đặc biệt:

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

Cách 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 })'

Cách 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())'

Điều này thật tuyệt! Ý tưởng tuyệt vời!
Anthony Kong

4

Đây là một thay thế trong Perl

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

Nó hoạt động với ví dụ được sửa đổi / mở rộng trong các câu hỏi có nhiều yếu tố "phiên bản" ở các độ sâu khác nhau.


Chậm, (mặc dù nhanh hơn xmlgrep)
Vi.

3

Cách thức táo bạo :

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

Dựa vào thụt lề chính xác của yêu cầu <version>


Cảm ơn lời đề nghị, nhưng tiếc là nó sẽ không trả lại những gì tôi muốn. Xin vui lòng xem mô hình pom cập nhật.
Anthony Kong

Trả về "1.0.74-SNAPSHOT". Lưu ý rằng tôi đã thay đổi tập lệnh sau khi đọc về nhiều <version>thứ.
Vi.

Lưu ý: giải pháp này được cung cấp "chỉ để giải trí" và không có ý định sử dụng trong sản phẩm thực tế. Sử dụng tốt hơn xml2 / xmlgrep / XML :: Giải pháp đơn giản.
Vi.

Cảm ơn! mặc dù đó là 'chỉ để giải trí' nhưng có lẽ đây là giải pháp 'phù hợp nhất' bởi vì nó có số lượng phụ thuộc tối thiểu: Nó chỉ yêu cầu perl ;-)
Anthony Kong

Còn việc làm nó từ Java thì sao? Sử dụng các tệp pom ngụ ý đã cài đặt JVM.
Vi.

3

Làm việc ra một giải pháp rất lót, vụng về

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"

Chiếc sed ở cuối rất xấu nhưng tôi không thể in ra văn bản của nút chỉ với mindom.

Cập nhật từ _Vi :

Phiên bản Python ít hack hơn:

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

Cập nhật từ tôi

Phiên bản khác:

    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

Cách 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

Nếu xsltproc có trên hệ thống của bạn và có thể là libxslt trên RHEL4, thì bạn có thể sử dụng nó và biểu định kiểu ở trên để xuất thẻ, tức là xsltproc x.xsl prom.xsl.
fpmurphy

2

nếu "Có rất nhiều thẻ phiên bản trong xml" thì tốt hơn bạn nên quên làm điều đó với "công cụ đơn giản" và biểu thức chính quy, điều đó sẽ không xảy ra.

thử con trăn này (không phụ thuộc):

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

Chính xác thì kịch bản này làm gì?
Simon Sheehan

nó tải XML dưới dạng cấu trúc DOM bằng cách sử dụng triển khai tối thiểu của Python: docs.python.org/l Library / xml.dom.minidom.html ý tưởng là lấy thẻ <project> duy nhất và sau đó lặp qua các nút con của nó (trực tiếp chỉ dành cho trẻ em) để tìm thẻ <phiên bản> mà chúng tôi đang tìm và không phải các thẻ khác có cùng tên ở những nơi khác.
Samus_

1

Đây là một lót sử dụng sed:

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

1
Dựa vào sự vắng mặt của các tham số trong các phần tử và các phần phụ <version>có thể chỉ nằm trong phần phụ thuộc.
Vi.

1

awk hoạt động tốt mà không cần sử dụng bất kỳ công cụ bổ sung.
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>

cách đơn giản và dễ đọc để lấy giá trị của <packaging>thẻ:

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

1
Điều này có vẻ hoạt động, nhưng hãy cẩn thận: Những gì nó làm được đặt dấu tách trường (FS) thành tập hợp các ký tự <và>; sau đó nó tìm thấy tất cả các dòng có từ "bao bì" trong đó và cung cấp cho bạn trường thứ ba.
SMerrill8

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

Ở đây, hãy thử điều này:

$TagElmnt - TagName
$FILE - xml file to parse

0

Tôi biết câu hỏi của bạn nói về Linux nhưng nếu bạn có nhu cầu thực hiện điều này trên Windows mà không cần bất kỳ công cụ của bên thứ 3 nào để bạn có thể đặt nó trong một tệp bó, Powershell có thể trích xuất bất kỳ nút nào từ tệp pom.xml của bạn như vậy :

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 hiện là mã nguồn mở và chạy trên Linux và các nền tảng khác. Chúng tôi sử dụng nó để xây dựng theo sở thích để bash, cygwin và ming64.
Charlweed

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

Các -ntùy chọn tránh in dòng không phù hợp; trận đấu đầu tiên ( /.../) nằm trên dòng trước văn bản mong muốn; các nlệnh bỏ qua để dòng tiếp theo, nơi schiết xuất thông tin liên quan thông qua một nhóm chụp ( \(...\)), và một backreference ( \1). pin ra, qbỏ.


2
Bạn có thể mở rộng câu trả lời của bạn để giải thích điều này? Cảm ơn.
fixer1234
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.