Idealnya, apa yang ingin saya lakukan adalah:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Idealnya, apa yang ingin saya lakukan adalah:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Jawaban:
Ini benar-benar hanya sebuah penjelasan dari jawaban Yuzem , tetapi saya tidak merasa bahwa banyak pengeditan ini harus dilakukan kepada orang lain, dan komentar tidak mengizinkan pemformatan, jadi ...
rdom () { local IFS=\> ; read -d \< E C ;}
Sebut saja "read_dom" alih-alih "rdom", kosongkan sedikit dan gunakan variabel yang lebih panjang:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
}
Oke jadi itu mendefinisikan fungsi yang disebut read_dom. Baris pertama membuat IFS (pemisah bidang input) lokal untuk fungsi ini dan mengubahnya ke>. Itu berarti bahwa ketika Anda membaca data alih-alih secara otomatis dipisah pada ruang, tab atau baris baru itu terbagi pada '>'. Baris berikutnya mengatakan untuk membaca input dari stdin, dan bukannya berhenti di baris baru, berhentilah ketika Anda melihat karakter '<' (the -d untuk flag pembatas). Apa yang dibaca kemudian dibagi menggunakan IFS dan ditugaskan ke variabel ENTITY dan CONTENT. Jadi, ambil yang berikut ini:
<tag>value</tag>
Panggilan pertama untuk read_dom
mendapatkan string kosong (karena '<' adalah karakter pertama). Itu terpecah oleh IFS menjadi hanya '', karena tidak ada karakter '>'. Baca lalu berikan string kosong ke kedua variabel. Panggilan kedua mendapatkan string 'tag> value'. Itu kemudian dibagi oleh IFS menjadi dua kolom 'tag' dan 'value'. Baca lalu tetapkan variabel seperti: ENTITY=tag
dan CONTENT=value
. Panggilan ketiga mendapat string '/ tag>'. Itu terpecah oleh IFS menjadi dua bidang '/ tag' dan ''. Baca lalu tetapkan variabel seperti: ENTITY=/tag
dan CONTENT=
. Panggilan keempat akan mengembalikan status bukan nol karena kami telah mencapai akhir file.
Sekarang loop while-nya membersihkan sedikit agar sesuai dengan yang di atas:
while read_dom; do
if [[ $ENTITY = "title" ]]; then
echo $CONTENT
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Baris pertama hanya mengatakan, "sementara fungsi read_dom mengembalikan status nol, lakukan hal berikut." Baris kedua memeriksa apakah entitas yang baru saja kita lihat adalah "judul". Baris berikutnya menggemakan konten tag. Empat garis keluar. Jika itu bukan entitas judul maka loop berulang di baris keenam. Kami mengarahkan "xhtmlfile.xhtml" ke input standar (untuk read_dom
fungsi) dan mengarahkan output standar ke "titleOfXHTMLPage.txt" (gema dari sebelumnya dalam loop).
Sekarang diberi yang berikut (mirip dengan apa yang Anda dapatkan dari daftar ember di S3) untuk input.xml
:
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>sth-items</Name>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>item-apple-iso@2x.png</Key>
<LastModified>2011-07-25T22:23:04.000Z</LastModified>
<ETag>"0032a28286680abee71aed5d059c6a09"</ETag>
<Size>1785</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
dan loop berikut:
while read_dom; do
echo "$ENTITY => $CONTENT"
done < input.xml
Anda harus mendapatkan:
=>
ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/" =>
Name => sth-items
/Name =>
IsTruncated => false
/IsTruncated =>
Contents =>
Key => item-apple-iso@2x.png
/Key =>
LastModified => 2011-07-25T22:23:04.000Z
/LastModified =>
ETag => "0032a28286680abee71aed5d059c6a09"
/ETag =>
Size => 1785
/Size =>
StorageClass => STANDARD
/StorageClass =>
/Contents =>
Jadi jika kita menulis sebuah while
loop seperti Yuzem:
while read_dom; do
if [[ $ENTITY = "Key" ]] ; then
echo $CONTENT
fi
done < input.xml
Kami akan mendapatkan daftar semua file di keranjang S3.
EDIT
Jika karena alasan tertentu local IFS=\>
tidak berhasil untuk Anda dan Anda menyetelnya secara global, Anda harus mengatur ulang di akhir fungsi seperti:
read_dom () {
ORIGINAL_IFS=$IFS
IFS=\>
read -d \< ENTITY CONTENT
IFS=$ORIGINAL_IFS
}
Jika tidak, setiap baris yang Anda lakukan nanti dalam skrip akan kacau.
EDIT 2
Untuk membagi pasangan nama / nilai atribut Anda dapat menambahkan read_dom()
seperti:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local ret=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $ret
}
Kemudian tulis fungsi Anda untuk menguraikan dan mendapatkan data yang Anda inginkan seperti ini:
parse_dom () {
if [[ $TAG_NAME = "foo" ]] ; then
eval local $ATTRIBUTES
echo "foo size is: $size"
elif [[ $TAG_NAME = "bar" ]] ; then
eval local $ATTRIBUTES
echo "bar type is: $type"
fi
}
Kemudian saat Anda read_dom
menelepon parse_dom
:
while read_dom; do
parse_dom
done
Kemudian diberikan contoh markup berikut:
<example>
<bar size="bar_size" type="metal">bars content</bar>
<foo size="1789" type="unknown">foos content</foo>
</example>
Anda harus mendapatkan hasil ini:
$ cat example.xml | ./bash_xml.sh
bar type is: metal
foo size is: 1789
EDIT 3 pengguna lain mengatakan mereka mengalami masalah dengan itu di FreeBSD dan menyarankan menyimpan status keluar dari membaca dan mengembalikannya di akhir read_dom seperti:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local RET=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $RET
}
Saya tidak melihat alasan mengapa itu tidak berhasil
IFS=\< read ...
saja:, yang hanya akan mengatur IFS untuk panggilan baca. (Perhatikan bahwa saya sama sekali tidak mendukung praktik penggunaan read
untuk mem-parsing xml, dan saya percaya melakukan hal itu penuh dengan bahaya dan harus dihindari.)
Anda dapat melakukannya dengan sangat mudah hanya menggunakan bash. Anda hanya perlu menambahkan fungsi ini:
rdom () { local IFS=\> ; read -d \< E C ;}
Sekarang Anda dapat menggunakan rdom seperti baca tetapi untuk dokumen html. Ketika dipanggil rdom akan menetapkan elemen ke variabel E dan konten ke var C.
Misalnya, untuk melakukan apa yang ingin Anda lakukan:
while rdom; do
if [[ $E = title ]]; then
echo $C
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Alat baris perintah yang dapat dipanggil dari skrip shell meliputi:
Saya juga menggunakan xmllint dan xsltproc dengan sedikit skrip transformasi XSL untuk melakukan pemrosesan XML dari baris perintah atau dalam skrip shell.
Anda dapat menggunakan utilitas xpath. Itu diinstal dengan paket Perl XML-XPath.
Pemakaian:
/usr/bin/xpath [filename] query
atau XMLStarlet . Untuk menginstalnya di opensuse gunakan:
sudo zypper install xmlstarlet
atau coba cnf xml
di platform lain.
xpath
yang sudah diinstal sebelumnya tidak cocok untuk digunakan sebagai komponen dalam skrip. Lihat misalnya stackoverflow.com/questions/15461737/… untuk penjelasan.
apt-get install xmlstarlet
Ini cukup ...
xpath xhtmlfile.xhtml '/html/head/title/text()' > titleOfXHTMLPage.txt
apt-get install libxml-xpath-perl
.
Lihat XML2 dari http://www.ofb.net/~egnor/xml2/ yang mengubah XML ke format berorientasi garis.
mulai dari jawaban chad, berikut ini adalah solusi kerja COMPLETE untuk mengurai UML, dengan penanganan komentar yang tepat, dengan hanya 2 fungsi kecil (lebih dari 2 bu Anda dapat mencampur semuanya). Saya tidak mengatakan chad tidak berfungsi sama sekali, tetapi memiliki terlalu banyak masalah dengan file XML yang diformat dengan buruk: Jadi Anda harus sedikit lebih rumit untuk menangani komentar dan spasi yang salah tempat / CR / TAB / dll.
Tujuan dari jawaban ini adalah untuk memberikan fungsi ready-2-use, out of the box untuk siapa saja yang membutuhkan parsing UML tanpa alat kompleks menggunakan perl, python atau apa pun. Bagi saya, saya tidak dapat menginstal cpan, atau modul perl untuk OS produksi lama yang saya kerjakan, dan python tidak tersedia.
Pertama, definisi kata-kata UML yang digunakan dalam posting ini:
<!-- comment... -->
<tag attribute="value">content...</tag>
EDIT: fungsi yang diperbarui, dengan pegangan:
xml_read_dom() {
# /programming/893585/how-to-parse-xml-in-bash
local ENTITY IFS=\>
if $ITSACOMMENT; then
read -d \< COMMENTS
COMMENTS="$(rtrim "${COMMENTS}")"
return 0
else
read -d \< ENTITY CONTENT
CR=$?
[ "x${ENTITY:0:1}x" == "x/x" ] && return 0
TAG_NAME=${ENTITY%%[[:space:]]*}
[ "x${TAG_NAME}x" == "x?xmlx" ] && TAG_NAME=xml
TAG_NAME=${TAG_NAME%%:*}
ATTRIBUTES=${ENTITY#*[[:space:]]}
ATTRIBUTES="${ATTRIBUTES//xmi:/}"
ATTRIBUTES="${ATTRIBUTES//xmlns:/}"
fi
# when comments sticks to !-- :
[ "x${TAG_NAME:0:3}x" == "x!--x" ] && COMMENTS="${TAG_NAME:3} ${ATTRIBUTES}" && ITSACOMMENT=true && return 0
# http://tldp.org/LDP/abs/html/string-manipulation.html
# INFO: oh wait it doesn't work on IBM AIX bash 3.2.16(1):
# [ "x${ATTRIBUTES:(-1):1}x" == "x/x" -o "x${ATTRIBUTES:(-1):1}x" == "x?x" ] && ATTRIBUTES="${ATTRIBUTES:0:(-1)}"
[ "x${ATTRIBUTES:${#ATTRIBUTES} -1:1}x" == "x/x" -o "x${ATTRIBUTES:${#ATTRIBUTES} -1:1}x" == "x?x" ] && ATTRIBUTES="${ATTRIBUTES:0:${#ATTRIBUTES} -1}"
return $CR
}
dan yang kedua:
xml_read() {
# /programming/893585/how-to-parse-xml-in-bash
ITSACOMMENT=false
local MULTIPLE_ATTR LIGHT FORCE_PRINT XAPPLY XCOMMAND XATTRIBUTE GETCONTENT fileXml tag attributes attribute tag2print TAGPRINTED attribute2print XAPPLIED_COLOR PROSTPROCESS USAGE
local TMP LOG LOGG
LIGHT=false
FORCE_PRINT=false
XAPPLY=false
MULTIPLE_ATTR=false
XAPPLIED_COLOR=g
TAGPRINTED=false
GETCONTENT=false
PROSTPROCESS=cat
Debug=${Debug:-false}
TMP=/tmp/xml_read.$RANDOM
USAGE="${C}${FUNCNAME}${c} [-cdlp] [-x command <-a attribute>] <file.xml> [tag | \"any\"] [attributes .. | \"content\"]
${nn[2]} -c = NOCOLOR${END}
${nn[2]} -d = Debug${END}
${nn[2]} -l = LIGHT (no \"attribute=\" printed)${END}
${nn[2]} -p = FORCE PRINT (when no attributes given)${END}
${nn[2]} -x = apply a command on an attribute and print the result instead of the former value, in green color${END}
${nn[1]} (no attribute given will load their values into your shell; use '-p' to print them as well)${END}"
! (($#)) && echo2 "$USAGE" && return 99
(( $# < 2 )) && ERROR nbaram 2 0 && return 99
# getopts:
while getopts :cdlpx:a: _OPT 2>/dev/null
do
{
case ${_OPT} in
c) PROSTPROCESS="${DECOLORIZE}" ;;
d) local Debug=true ;;
l) LIGHT=true; XAPPLIED_COLOR=END ;;
p) FORCE_PRINT=true ;;
x) XAPPLY=true; XCOMMAND="${OPTARG}" ;;
a) XATTRIBUTE="${OPTARG}" ;;
*) _NOARGS="${_NOARGS}${_NOARGS+, }-${OPTARG}" ;;
esac
}
done
shift $((OPTIND - 1))
unset _OPT OPTARG OPTIND
[ "X${_NOARGS}" != "X" ] && ERROR param "${_NOARGS}" 0
fileXml=$1
tag=$2
(( $# > 2 )) && shift 2 && attributes=$*
(( $# > 1 )) && MULTIPLE_ATTR=true
[ -d "${fileXml}" -o ! -s "${fileXml}" ] && ERROR empty "${fileXml}" 0 && return 1
$XAPPLY && $MULTIPLE_ATTR && [ -z "${XATTRIBUTE}" ] && ERROR param "-x command " 0 && return 2
# nb attributes == 1 because $MULTIPLE_ATTR is false
[ "${attributes}" == "content" ] && GETCONTENT=true
while xml_read_dom; do
# (( CR != 0 )) && break
(( PIPESTATUS[1] != 0 )) && break
if $ITSACOMMENT; then
# oh wait it doesn't work on IBM AIX bash 3.2.16(1):
# if [ "x${COMMENTS:(-2):2}x" == "x--x" ]; then COMMENTS="${COMMENTS:0:(-2)}" && ITSACOMMENT=false
# elif [ "x${COMMENTS:(-3):3}x" == "x-->x" ]; then COMMENTS="${COMMENTS:0:(-3)}" && ITSACOMMENT=false
if [ "x${COMMENTS:${#COMMENTS} - 2:2}x" == "x--x" ]; then COMMENTS="${COMMENTS:0:${#COMMENTS} - 2}" && ITSACOMMENT=false
elif [ "x${COMMENTS:${#COMMENTS} - 3:3}x" == "x-->x" ]; then COMMENTS="${COMMENTS:0:${#COMMENTS} - 3}" && ITSACOMMENT=false
fi
$Debug && echo2 "${N}${COMMENTS}${END}"
elif test "${TAG_NAME}"; then
if [ "x${TAG_NAME}x" == "x${tag}x" -o "x${tag}x" == "xanyx" ]; then
if $GETCONTENT; then
CONTENT="$(trim "${CONTENT}")"
test ${CONTENT} && echo "${CONTENT}"
else
# eval local $ATTRIBUTES => eval test "\"\$${attribute}\"" will be true for matching attributes
eval local $ATTRIBUTES
$Debug && (echo2 "${m}${TAG_NAME}: ${M}$ATTRIBUTES${END}"; test ${CONTENT} && echo2 "${m}CONTENT=${M}$CONTENT${END}")
if test "${attributes}"; then
if $MULTIPLE_ATTR; then
# we don't print "tag: attr=x ..." for a tag passed as argument: it's usefull only for "any" tags so then we print the matching tags found
! $LIGHT && [ "x${tag}x" == "xanyx" ] && tag2print="${g6}${TAG_NAME}: "
for attribute in ${attributes}; do
! $LIGHT && attribute2print="${g10}${attribute}${g6}=${g14}"
if eval test "\"\$${attribute}\""; then
test "${tag2print}" && ${print} "${tag2print}"
TAGPRINTED=true; unset tag2print
if [ "$XAPPLY" == "true" -a "${attribute}" == "${XATTRIBUTE}" ]; then
eval ${print} "%s%s\ " "\${attribute2print}" "\${${XAPPLIED_COLOR}}\"\$(\$XCOMMAND \$${attribute})\"\${END}" && eval unset ${attribute}
else
eval ${print} "%s%s\ " "\${attribute2print}" "\"\$${attribute}\"" && eval unset ${attribute}
fi
fi
done
# this trick prints a CR only if attributes have been printed durint the loop:
$TAGPRINTED && ${print} "\n" && TAGPRINTED=false
else
if eval test "\"\$${attributes}\""; then
if $XAPPLY; then
eval echo "\${g}\$(\$XCOMMAND \$${attributes})" && eval unset ${attributes}
else
eval echo "\$${attributes}" && eval unset ${attributes}
fi
fi
fi
else
echo eval $ATTRIBUTES >>$TMP
fi
fi
fi
fi
unset CR TAG_NAME ATTRIBUTES CONTENT COMMENTS
done < "${fileXml}" | ${PROSTPROCESS}
# http://mywiki.wooledge.org/BashFAQ/024
# INFO: I set variables in a "while loop" that's in a pipeline. Why do they disappear? workaround:
if [ -s "$TMP" ]; then
$FORCE_PRINT && ! $LIGHT && cat $TMP
# $FORCE_PRINT && $LIGHT && perl -pe 's/[[:space:]].*?=/ /g' $TMP
$FORCE_PRINT && $LIGHT && sed -r 's/[^\"]*([\"][^\"]*[\"][,]?)[^\"]*/\1 /g' $TMP
. $TMP
rm -f $TMP
fi
unset ITSACOMMENT
}
dan terakhir, fungsi rtrim, trim dan echo2 (to stderr):
rtrim() {
local var=$@
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
trim() {
local var=$@
var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
echo2() { echo -e "$@" 1>&2; }
oh dan Anda akan memerlukan beberapa variabel dinamis pewarnaan rapi untuk didefinisikan pada awalnya, dan diekspor juga:
set -a
TERM=xterm-256color
case ${UNAME} in
AIX|SunOS)
M=$(${print} '\033[1;35m')
m=$(${print} '\033[0;35m')
END=$(${print} '\033[0m')
;;
*)
m=$(tput setaf 5)
M=$(tput setaf 13)
# END=$(tput sgr0) # issue on Linux: it can produces ^[(B instead of ^[[0m, more likely when using screenrc
END=$(${print} '\033[0m')
;;
esac
# 24 shades of grey:
for i in $(seq 0 23); do eval g$i="$(${print} \"\\033\[38\;5\;$((232 + i))m\")" ; done
# another way of having an array of 5 shades of grey:
declare -a colorNums=(238 240 243 248 254)
for num in 0 1 2 3 4; do nn[$num]=$(${print} "\033[38;5;${colorNums[$num]}m"); NN[$num]=$(${print} "\033[48;5;${colorNums[$num]}m"); done
# piped decolorization:
DECOLORIZE='eval sed "s,${END}\[[0-9;]*[m|K],,g"'
Entah Anda tahu cara membuat fungsi dan memuatnya melalui FPATH (ksh) atau emulasi FPATH (bash)
Jika tidak, cukup salin / tempel semua yang ada di baris perintah.
xml_read [-cdlp] [-x command <-a attribute>] <file.xml> [tag | "any"] [attributes .. | "content"]
-c = NOCOLOR
-d = Debug
-l = LIGHT (no \"attribute=\" printed)
-p = FORCE PRINT (when no attributes given)
-x = apply a command on an attribute and print the result instead of the former value, in green color
(no attribute given will load their values into your shell as $ATTRIBUTE=value; use '-p' to print them as well)
xml_read server.xml title content # print content between <title></title>
xml_read server.xml Connector port # print all port values from Connector tags
xml_read server.xml any port # print all port values from any tags
Dengan mode Debug (-d) komentar dan atribut yang diuraikan dicetak ke stderr
./read_xml.sh: line 22: (-1): substring expression < 0
:?
[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
Saya tidak mengetahui adanya alat parsing XML shell murni. Jadi Anda kemungkinan besar akan membutuhkan alat yang ditulis dalam bahasa lain.
Modul XML :: Twig Perl saya dilengkapi dengan alat seperti ini:, di xml_grep
mana Anda mungkin akan menulis apa yang Anda inginkan xml_grep -t '/html/head/title' xhtmlfile.xhtml > titleOfXHTMLPage.txt
( -t
opsi memberi Anda hasilnya sebagai teks, bukan xml)
Alat baris perintah lainnya adalah Xidel baru saya . Ini juga mendukung XPath 2 dan XQuery, bertentangan dengan xpath / xmlstarlet yang telah disebutkan.
Judulnya bisa dibaca seperti:
xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt
Dan itu juga memiliki fitur keren untuk mengekspor beberapa variabel ke bash. Sebagai contoh
eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )
set $title
ke judul dan $imgcount
jumlah gambar dalam file, yang harus sefleksibel parsing langsung di bash.
Setelah beberapa penelitian untuk terjemahan antara Linux dan format Windows jalur file dalam file XML saya menemukan tutorial dan solusi menarik pada:
Walaupun ada beberapa utilitas konsol siap pakai yang mungkin melakukan apa yang Anda inginkan, mungkin akan memakan waktu lebih sedikit untuk menulis beberapa baris kode dalam bahasa pemrograman tujuan umum seperti Python yang dapat dengan mudah diperluas dan disesuaikan dengan kebutuhanmu.
Berikut ini adalah skrip python yang digunakan lxml
untuk parsing - dibutuhkan nama file atau URL sebagai parameter pertama, ekspresi XPath sebagai parameter kedua, dan mencetak string / node yang cocok dengan ekspresi yang diberikan.
#!/usr/bin/env python
import sys
from lxml import etree
tree = etree.parse(sys.argv[1])
xpath_expression = sys.argv[2]
# a hack allowing to access the
# default namespace (if defined) via the 'p:' prefix
# E.g. given a default namespaces such as 'xmlns="http://maven.apache.org/POM/4.0.0"'
# an XPath of '//p:module' will return all the 'module' nodes
ns = tree.getroot().nsmap
if ns.keys() and None in ns:
ns['p'] = ns.pop(None)
# end of hack
for e in tree.xpath(xpath_expression, namespaces=ns):
if isinstance(e, str):
print(e)
else:
print(e.text and e.text.strip() or etree.tostring(e, pretty_print=True))
lxml
dapat diinstal dengan pip install lxml
. Di ubuntu Anda bisa menggunakan sudo apt install python-lxml
.
python xpath.py myfile.xml "//mynode"
lxml
juga menerima URL sebagai input:
python xpath.py http://www.feedforall.com/sample.xml "//link"
Catatan : Jika XML Anda memiliki namespace default tanpa awalan (mis.
xmlns=http://abc...
) Maka Anda harus menggunakanp
awalan (disediakan oleh 'hack') dalam ekspresi Anda, misalnya//p:module
untuk mendapatkan modul daripom.xml
file. Jikap
awalan sudah dipetakan dalam XML Anda, maka Anda harus memodifikasi skrip untuk menggunakan awalan lain.
Skrip satu kali yang melayani tujuan sempit mengekstraksi nama modul dari file apache maven. Perhatikan bagaimana nama simpul ( module
) diawali dengan namespace default {http://maven.apache.org/POM/4.0.0}
:
pom.xml :
<?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/xsd/maven-4.0.0.xsd">
<modules>
<module>cherries</module>
<module>bananas</module>
<module>pears</module>
</modules>
</project>
module_extractor.py :
from lxml import etree
for _, e in etree.iterparse(open("pom.xml"), tag="{http://maven.apache.org/POM/4.0.0}module"):
print(e.text)
pip install
over apt-get
atau yum
panggilan ekstra . Terima kasih!
Metode Yuzem dapat ditingkatkan dengan membalik urutan <
dan >
tanda - tanda dalam rdom
fungsi dan tugas variabel, sehingga:
rdom () { local IFS=\> ; read -d \< E C ;}
menjadi:
rdom () { local IFS=\< ; read -d \> C E ;}
Jika parsing tidak dilakukan seperti ini, tag terakhir dalam file XML tidak pernah tercapai. Ini bisa bermasalah jika Anda berniat untuk menghasilkan file XML lain di akhir while
loop.
Ini berfungsi jika Anda menginginkan atribut XML:
$ cat alfa.xml
<video server="asdf.com" stream="H264_400.mp4" cdn="limelight"/>
$ sed 's.[^ ]*..;s./>..' alfa.xml > alfa.sh
$ . ./alfa.sh
$ echo "$stream"
H264_400.mp4
Meskipun sepertinya "tidak pernah menguraikan XML, JSON ... dari bash tanpa alat yang tepat" adalah saran yang bagus, saya tidak setuju. Jika ini adalah pekerjaan sampingan, itu adalah pinggang untuk mencari alat yang tepat, kemudian mempelajarinya ... Awk dapat melakukannya dalam hitungan menit. Program saya harus mengerjakan semua data yang disebutkan di atas dan lebih banyak lagi. Sial, saya tidak ingin menguji 30 alat untuk mem-parsing 5-7-10 format yang berbeda yang saya butuhkan jika saya dapat mengatasi masalah dalam hitungan menit. Saya tidak peduli dengan XML, JSON atau apa pun! Saya butuh solusi tunggal untuk semuanya.
Sebagai contoh: Program SmartHome saya mengelola rumah kami. Saat melakukannya, ia membaca sejumlah besar data dalam berbagai format berbeda yang tidak dapat saya kendalikan. Saya tidak pernah menggunakan alat khusus yang berdedikasi karena saya tidak ingin menghabiskan lebih dari beberapa menit untuk membaca data yang saya butuhkan. Dengan penyesuaian FS dan RS, solusi awk ini berfungsi sempurna untuk semua format teks. Tapi, itu mungkin bukan jawaban yang tepat ketika tugas utama Anda adalah bekerja terutama dengan banyak data dalam format itu!
Masalah parsing XML dari bash yang saya hadapi kemarin. Inilah cara saya melakukannya untuk format data hierarkis apa pun. Sebagai bonus - saya menetapkan data langsung ke variabel dalam skrip bash.
Agar lebih mudah dibaca, saya akan menyajikan solusi secara bertahap. Dari data tes OP, saya membuat file: test.xml
Parsing mengatakan XML dalam bash dan mengekstraksi data dalam 90 karakter:
awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml
Saya biasanya menggunakan versi yang lebih mudah dibaca karena lebih mudah untuk dimodifikasi dalam kehidupan nyata karena saya sering perlu menguji secara berbeda:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml
Saya tidak peduli bagaimana formatnya. Saya hanya mencari solusi paling sederhana. Dalam kasus khusus ini, saya dapat melihat dari data bahwa baris baru adalah pemisah rekaman (RS) dan bidang pembatas <> (FS). Dalam kasus asli saya, saya memiliki pengindeksan rumit dari 6 nilai dalam dua catatan, yang berkaitan dengan mereka, temukan ketika data ada ditambah bidang (catatan) mungkin atau mungkin tidak ada. Butuh 4 baris awk untuk menyelesaikan masalah dengan sempurna. Jadi, sesuaikan ide dengan setiap kebutuhan sebelum menggunakannya!
Bagian kedua hanya terlihat ada string yang diinginkan dalam garis (RS) dan jika demikian, mencetak bidang yang diperlukan (FS). Di atas butuh waktu sekitar 30 detik untuk menyalin dan beradaptasi dari perintah terakhir yang saya gunakan dengan cara ini (4 kali lebih lama). Dan itu dia! Dilakukan dalam 90 karakter.
Tapi, saya selalu perlu memasukkan data ke dalam variabel dalam skrip saya. Saya pertama kali menguji konstruksi seperti:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml
Dalam beberapa kasus saya menggunakan printf daripada print. Ketika saya melihat semuanya terlihat baik, saya hanya selesai menetapkan nilai ke variabel. Saya tahu banyak yang berpikir "eval" adalah "jahat", tidak perlu berkomentar :) Trik bekerja dengan baik pada keempat jaringan saya selama bertahun-tahun. Tetapi teruslah belajar jika Anda tidak mengerti mengapa ini bisa menjadi praktik yang buruk! Termasuk tugas variabel bash dan spasi yang cukup, solusi saya perlu 120 karakter untuk melakukan semuanya.
eval $( awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml ); echo "host: $host, username: $username, password: $password dbname: $dbname"