Grep dan Sed Setara untuk Pemrosesan Baris Perintah XML

147

Saat melakukan shell scripting, biasanya data akan berada dalam file catatan baris tunggal seperti csv. Sangat sederhana untuk menangani data ini dengan grepdan sed. Tapi saya harus sering berurusan dengan XML, jadi saya benar-benar ingin cara mengakses skrip ke data XML melalui baris perintah. Apa alat terbaik?

Joseph Holsten
sumber
xml_grep baik untuk grepping, sebagaimana dinyatakan dalam stackoverflow.com/a/2222224/871134
Deleplace

Jawaban:

105

Saya telah menemukan xmlstarlet cukup bagus dalam hal semacam ini.

http://xmlstar.sourceforge.net/

Seharusnya tersedia di sebagian besar repositori distro. Tutorial pengantar ada di sini:

http://www.ibm.com/developerworks/library/x-starlet.html

Russ
sumber
1
Saya pikir saya menunjukkan bahwa ada binari Windows yang tersedia di situs Sourceforge.
Steve Bennett
Namun, tidak mendukung XQuery, sejauh yang saya tahu.
Steve Bennett
@SteveBennett memang tidak, tetapi fitur yang ditambahkannya di atas XPath mentah cukup baik untuk membuatnya kompetitif dengan "grep and sed". Jika Anda menginginkan kebaikan, kebaikan mewah dari XQuery ... yah, itu lebih mirip dengan XML untuk perl atau awk. :)
Charles Duffy
36

Beberapa alat yang menjanjikan:

  • nokogiri : parsing HTML / XML DOM di ruby ​​menggunakan pemilih XPath & CSS

  • hpricot : usang

  • fxgrep : Menggunakan sintaks seperti XPath untuk meminta dokumen. Ditulis dalam SML, jadi pemasangan mungkin sulit.

  • LT XML : XML toolkit berasal dari alat SGML, termasuk sggrep, sgsort, xmlnormdan lain-lain. Menggunakan sintaks kueri sendiri. Dokumentasinya sangat formal. Ditulis dalam C. LT XML 2 mengklaim dukungan XPath, XInclude dan standar W3C lainnya.

  • xmlgrep2 : pencarian sederhana dan kuat dengan XPath. Ditulis dalam Perl menggunakan XML :: LibXML dan libxml2.

  • XQSharp : Mendukung XQuery, ekstensi ke XPath. Ditulis untuk .NET Framework.

  • xml-coreutils : toolkit Laird Breyer setara dengan GNU coreutils. Dibahas dalam esai yang menarik tentang apa yang harus dimasukkan oleh perangkat ideal.

  • xmldiff : Alat sederhana untuk membandingkan dua file xml.

  • xmltk : tampaknya tidak memiliki paket di debian, ubuntu, fedora, atau macports, belum memiliki rilis sejak 2007, dan menggunakan otomatisasi pembuatan non-portabel.

xml-coreutils tampaknya yang paling terdokumentasi dan paling berorientasi UNIX.

Joseph Holsten
sumber
1
Tidak bisakah Anda membuat skrip pembungkus untuk program Ruby, dan meneruskan array argumen dalam skrip ke hpricot? Misalnya, dalam skrip shell PHP, sesuatu seperti yang berikut ini akan berfungsi: <? Php / path / to / hpricot $ argv?>
alastairs
25

Ke daftar Joseph Holsten yang sangat baik, saya menambahkan skrip baris perintah xpath yang datang dengan perpustakaan Perl XML :: XPath. Cara hebat untuk mengekstrak informasi dari file XML:

 xpath -q -e '/entry[@xml:lang="fr"]' *xml
bortzmeyer
sumber
3
Ini diinstal secara default di osx, tetapi tanpa -q -eopsi. Contoh, dapatkan nilai atribut "paket" dari simpul "manifes" di "AndroidManifest.xml":xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
antonj
25

Ada juga xml2dan 2xmlpasangan. Ini akan memungkinkan alat pengeditan string biasa untuk memproses XML.

Contoh. q.xml:

<?xml version="1.0"?>
<foo>
    text
    more text
    <textnode>ddd</textnode><textnode a="bv">dsss</textnode>
    <![CDATA[ asfdasdsa <foo> sdfsdfdsf <bar> ]]>
</foo>

xml2 < q.xml

/foo=
/foo=   text
/foo=   more text
/foo=   
/foo/textnode=ddd
/foo/textnode
/foo/textnode/@a=bv
/foo/textnode=dsss
/foo=
/foo=    asfdasdsa <foo> sdfsdfdsf <bar> 
/foo=

xml2 < q.xml | grep textnode | sed 's!/foo!/bar/baz!' | 2xml

<bar><baz><textnode>ddd</textnode><textnode a="bv">dsss</textnode></baz></bar>

PS Ada juga html2/ 2html.

Vi.
sumber
@ Joseph Holsten Ya. Ini memungkinkan peretasan dengan XML tanpa memikirkan hal-hal XPath.
Vi.
Bagus! Saya telah memfokuskan pada alat yang tidak menggunakan format perantara, tetapi gagasan tentang kesetiaan tinggi, representasi xml yang berorientasi garis sepertinya merupakan cara yang bagus untuk tetap menggunakan grep dan sed nyata. Sudahkah Anda mencoba pyxie? Bagaimana cara membandingkannya? Adakah representasi berorientasi garis lain? Apakah Anda menganggap ini lebih baik daripada hanya mengganti xml baris baru dengan entitas (& # 10;)? Ini akan memungkinkan Anda menempelkan catatan pada baris yang sama setidaknya. Oh, dan bisakah Anda mengedit posting Anda untuk menyertakan tautan ke proyek?
Joseph Holsten
@ Joseph Holsten Tidak, saya tidak berpikir format pyxie akan lebih berguna daripada format xml2. xml2 menyediakan "path lengkap" dalam elemen XML bersarang, jadi izinkan pencocokan dan substitusi yang lebih berorientasi baris. Juga 2xmldapat dengan mudah membuat kembali XML dari xml2output parsial (difilter) .
Vi.
5
+1 Saya tidak dapat cukup mengungguli ini ... cat foo.xml | xml2 | grep /bar | 2xml- memberi Anda struktur yang sama seperti aslinya, tetapi semua elemen telah dilucuti kecuali elemen "bilah". Luar biasa.
mogsie
14

Anda dapat menggunakan xmllint:

xmllint --xpath //title books.xml

Harus dibundel dengan sebagian besar distro, dan juga dibundel dengan Cygwin.

$ xmllint --version
xmllint: using libxml version 20900

Lihat:

$ xmllint
Usage : xmllint [options] XMLfiles ...
        Parse the XML files and output the result of the parsing
        --version : display the version of the XML library used
        --debug : dump a debug tree of the in-memory document
        ...
        --schematron schema : do validation against a schematron
        --sax1: use the old SAX1 interfaces for processing
        --sax: do not build a tree but work just at the SAX level
        --oldxml10: use XML-1.0 parsing rules before the 5th edition
        --xpath expr: evaluate the XPath expression, inply --noout
Dave Jarvis
sumber
2
Tidak ada --xpathargumen untuk xmllint: manpagez.com/man/1/xmllint
Miserable Variable
1
@MiserableVariable: Halaman manual tidak benar. Saya hanya melihat halaman manual untuk versi saya: argumen xpath tidak terdaftar. Ini adalah kesalahan dokumentasi. Coba jalankan program, sebagai gantinya.
Dave Jarvis
2
@MiserableVariable --xpathadalah tambahan yang cukup baru dan mis. Tidak dalam versi RHEL 6 dari xmllint.
Daniel Beck
2
Lebih tepatnya, xmllint --xpathdiperkenalkan di libxml2 2.7.7 (tahun 2010).
marbu
9

Jika Anda mencari solusi pada Windows, Powershell memiliki fungsionalitas bawaan untuk membaca dan menulis XML.

test.xml:

<root>
  <one>I like applesauce</one>
  <two>You sure bet I do!</two>
</root>

Script Powershell:

# load XML file into local variable and cast as XML type.
$doc = [xml](Get-Content ./test.xml)

$doc.root.one                                   #echoes "I like applesauce"
$doc.root.one = "Who doesn't like applesauce?"  #replace inner text of <one> node

# create new node...
$newNode = $doc.CreateElement("three")
$newNode.set_InnerText("And don't you forget it!")

# ...and position it in the hierarchy
$doc.root.AppendChild($newNode)

# write results to disk
$doc.save("./testNew.xml")

testNew.xml:

<root>
  <one>Who likes applesauce?</one>
  <two>You sure bet I do!</two>
  <three>And don't you forget it!</three>
</root>

Sumber: /server/26976/update-xml-from-the-command-line-windows

Tanah liat
sumber
berjuang dengan berbagai alat linux selama beberapa jam sebelum beralih ke Powershell. Saya terkejut ini sangat sulit - linux cmd-line biasanya sangat bagus tapi sepertinya ada lubang di sini. Catatan: Use case untuk saya adalah: 1) cari node dengan xpath, 2) hapus jika ditemukan, 3) tambahkan node baru, 4) simpan file. Saya sedang memperbarui banyak konfigurasi solr. Jika ada yang tahu tentang cara mudah / dapat diandalkan untuk melakukan ini saya semua telinga
Richard Hauer
Wow, ini benar-benar berjinjit ke jalur solusi yang dapat diterima. Tapi jujur, saya mungkin akan menerimanya jika itu terlihat seperti xps $doc .root.one xps $doc 'AppendChild("three")'dan xps $doc '.three.set_InnerText("And don't you forget it!")', yang jelas lebih rendah!
Joseph Holsten
6

Tergantung pada apa yang ingin Anda lakukan.

XSLT mungkin cara yang harus dilakukan, tetapi ada kurva belajar. Coba xsltproc dan perhatikan bahwa Anda dapat memberikan parameter.

Adrian Mouat
sumber
4

Ada juga saxon-lintdari command line dengan kemampuan untuk menggunakan XPath 3.0 / XQuery 3.0. (Alat baris perintah lainnya menggunakan XPath 1.0).

CONTOH:

http / html:

$ saxon-lint --html --xpath 'count(//a)' http://stackoverflow.com/q/91791
328

xml:

$ saxon-lint --xpath '//a[@class="x"]' file.xml
Gilles Quenot
sumber
3

XQuery mungkin solusi yang bagus. Ini (relatif) mudah dipelajari dan merupakan standar W3C.

Saya akan merekomendasikan XQSharp untuk prosesor baris perintah.

Oliver Hallam
sumber
1
BaseX juga memiliki prosesor XQuery baris-perintah (selain mode database-nya), dan tetap up-to-date dengan versi standar terbaru (mengikuti konsep XQuery 3.0 yang berkembang dengan sangat dekat).
Charles Duffy
3

Saya pertama kali menggunakan xmlstarlet dan masih menggunakannya. Ketika query menjadi sulit, saya perlu XML ini xpath2 dan XQuery fitur dukungan yang saya beralih ke xidel http://www.videlibri.de/xidel.html

kebenaranadjustr
sumber
1

Grep Setara

Anda dapat mendefinisikan fungsi bash, katakan "xp" ("xpath") yang membungkus beberapa kode python3. Untuk menggunakannya Anda perlu menginstal python3 dan python-lxml. Manfaat:

  1. pencocokan regex yang kurang Anda miliki misalnya xmllint.
  2. Gunakan sebagai filter (dalam pipa) pada commandline

Mudah dan kuat untuk digunakan seperti ini:

xmldoc=$(cat <<EOF
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
EOF
)
selection='//*[namespace-uri()="http://www.sample.com/" and local-name()="job" and re:test(.,"^pro.*ing$")]/text()'
echo "$xmldoc" | xp "$selection"
# prints programming

xp () terlihat seperti ini:

xp()
{ 
local selection="$1";
local xmldoc;
if ! [[ -t 0 ]]; then
    read -rd '' xmldoc;
else
    xmldoc="$2";
fi;
python3 <(printf '%b' "from lxml.html import tostring\nfrom lxml import etree\nfrom sys import stdin\nregexpNS = \"http://exslt.org/regular-expressions\"\ntree = etree.parse(stdin)\nfor e in tree.xpath('""$selection""', namespaces={'re':regexpNS}):\n  if isinstance(e, str):\n    print(e)\n  else:\n    print(tostring(e).decode('UTF-8'))") <<< "$xmldoc"
}

Sed Setara

Pertimbangkan untuk menggunakan xq yang memberi Anda kekuatan penuh dari "bahasa pemrograman" jq. Jika Anda memiliki python-pip diinstal, Anda dapat menginstal xq dengan pip install yq , maka dalam contoh di bawah ini kami mengganti "Simpan Akun" dengan "Simpan Akun 2":

xmldoc=$(cat <<'EOF'
<resources>
    <string name="app_name">Keep Accounts</string>
    <string name="login">"login"</string>
    <string name="login_password">"password:"</string>
    <string name="login_account_hint">input to login</string>
    <string name="login_password_hint">input your password</string>
    <string name="login_fail">login failed</string>
</resources>
EOF
)
echo "$xmldoc" | xq '.resources.string = ([.resources.string[]|select(."#text" == "Keep Accounts") ."#text" = "Keep Accounts 2"])' -x
methuselah-0
sumber
-1

JEdit memiliki plugin yang disebut "XQuery" yang menyediakan fungsionalitas query untuk dokumen XML.

Tidak cukup baris perintah, tetapi berhasil!

Ben
sumber
Sementara JEdit mungkin memiliki cara untuk mencari melalui file, itu tidak membuatnya menjadi pesaing grep(1).
Joseph Holsten