Lý tưởng nhất, những gì tôi muốn có thể làm là:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Lý tưởng nhất, những gì tôi muốn có thể làm là:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Câu trả lời:
Đây thực sự chỉ là một lời giải thích cho câu trả lời của Yuzem , nhưng tôi không cảm thấy việc chỉnh sửa này nên được thực hiện cho người khác và các bình luận không cho phép định dạng, vì vậy ...
rdom () { local IFS=\> ; read -d \< E C ;}
Hãy gọi đó là "read_dom" thay vì "rdom", bỏ nó ra một chút và sử dụng các biến dài hơn:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
}
Được rồi để nó định nghĩa một hàm gọi là read_dom. Dòng đầu tiên làm cho IFS (dấu tách trường đầu vào) cục bộ thành hàm này và thay đổi nó thành>. Điều đó có nghĩa là khi bạn đọc dữ liệu thay vì tự động được phân chia trên không gian, tab hoặc dòng mới, nó sẽ được phân chia trên '>'. Dòng tiếp theo nói để đọc đầu vào từ stdin và thay vì dừng ở một dòng mới, hãy dừng lại khi bạn thấy ký tự '<' (cờ -d cho cờ phân cách). Những gì được đọc sau đó được phân tách bằng IFS và được gán cho biến ENTITY và NỘI DUNG. Vì vậy, hãy làm như sau:
<tag>value</tag>
Cuộc gọi đầu tiên để read_dom
nhận một chuỗi trống (vì '<' là ký tự đầu tiên). Điều đó bị IFS chia thành '', vì không có ký tự '>'. Đọc sau đó gán một chuỗi rỗng cho cả hai biến. Cuộc gọi thứ hai nhận được chuỗi 'tag> value'. Điều đó được phân chia sau đó bởi IFS thành hai trường 'tag' và 'value'. Đọc sau đó gán các biến như: ENTITY=tag
và CONTENT=value
. Cuộc gọi thứ ba nhận được chuỗi '/ tag>'. Điều đó được phân chia bởi IFS thành hai trường '/ tag' và ''. Đọc sau đó gán các biến như: ENTITY=/tag
và CONTENT=
. Cuộc gọi thứ tư sẽ trả về trạng thái khác không vì chúng tôi đã kết thúc tập tin.
Bây giờ vòng lặp while của anh đã dọn sạch một chút để khớp với điều trên:
while read_dom; do
if [[ $ENTITY = "title" ]]; then
echo $CONTENT
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Dòng đầu tiên chỉ nói, "trong khi hàm read_dom chuyển trạng thái 0, hãy làm như sau." Dòng thứ hai kiểm tra xem thực thể chúng ta vừa thấy là "tiêu đề". Dòng tiếp theo lặp lại nội dung của thẻ. Bốn dòng thoát. Nếu nó không phải là thực thể tiêu đề thì vòng lặp lặp lại trên dòng thứ sáu. Chúng tôi chuyển hướng "xhtmlfile.xhtml" thành đầu vào tiêu chuẩn (cho read_dom
chức năng) và chuyển hướng đầu ra tiêu chuẩn sang "titleOfXHTMLPage.txt" (tiếng vang từ trước đó trong vòng lặp).
Bây giờ được đưa ra như sau (tương tự với những gì bạn nhận được từ việc liệt kê một nhóm trên S3) cho 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>
và vòng lặp sau:
while read_dom; do
echo "$ENTITY => $CONTENT"
done < input.xml
Bạn sẽ nhận được:
=>
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 =>
Vì vậy, nếu chúng tôi đã viết một while
vòng lặp như của Yuzem:
while read_dom; do
if [[ $ENTITY = "Key" ]] ; then
echo $CONTENT
fi
done < input.xml
Chúng tôi sẽ nhận được một danh sách tất cả các tệp trong nhóm S3.
EDIT
Nếu vì một lý do local IFS=\>
nào đó không phù hợp với bạn và bạn đặt nó trên toàn cầu, bạn nên đặt lại nó ở cuối chức năng như:
read_dom () {
ORIGINAL_IFS=$IFS
IFS=\>
read -d \< ENTITY CONTENT
IFS=$ORIGINAL_IFS
}
Nếu không, bất kỳ dòng chia tách nào bạn thực hiện sau đó trong tập lệnh sẽ bị rối tung.
EDIT 2
Để tách các cặp tên / giá trị thuộc tính, bạn có thể gia tăng read_dom()
tương tự như vậy:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local ret=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $ret
}
Sau đó viết hàm của bạn để phân tích cú pháp và lấy dữ liệu bạn muốn như thế này:
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
}
Sau đó, trong khi bạn read_dom
gọi parse_dom
:
while read_dom; do
parse_dom
done
Sau đó đưa ra đánh dấu ví dụ sau:
<example>
<bar size="bar_size" type="metal">bars content</bar>
<foo size="1789" type="unknown">foos content</foo>
</example>
Bạn sẽ nhận được đầu ra này:
$ cat example.xml | ./bash_xml.sh
bar type is: metal
foo size is: 1789
EDIT 3 một người dùng khác cho biết họ gặp vấn đề với nó trong FreeBSD và đề nghị lưu trạng thái thoát khỏi đọc và trả lại vào cuối read_dom như:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local RET=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $RET
}
Tôi không thấy lý do tại sao điều đó không làm việc
IFS=\< read ...
, sẽ chỉ đặt IFS cho cuộc gọi đọc. (Lưu ý rằng tôi không có cách nào chứng thực việc sử dụng read
để phân tích xml, và tôi tin rằng làm như vậy là đầy rủi ro và nên tránh.)
Bạn có thể làm điều đó rất dễ dàng chỉ bằng bash. Bạn chỉ phải thêm chức năng này:
rdom () { local IFS=\> ; read -d \< E C ;}
Bây giờ bạn có thể sử dụng rdom như đọc nhưng cho các tài liệu html. Khi được gọi rdom sẽ gán phần tử cho biến E và nội dung cho var C.
Ví dụ: để làm những gì bạn muốn làm:
while rdom; do
if [[ $E = title ]]; then
echo $C
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Các công cụ dòng lệnh có thể được gọi từ các kịch bản shell bao gồm:
Tôi cũng sử dụng xmllint và xsltproc với các tập lệnh biến đổi XSL nhỏ để xử lý XML từ dòng lệnh hoặc trong các tập lệnh shell.
Bạn có thể sử dụng tiện ích xpath. Nó được cài đặt với gói Perl XML-XPath.
Sử dụng:
/usr/bin/xpath [filename] query
hoặc XMLStarlet . Để cài đặt nó trên openuse sử dụng:
sudo zypper install xmlstarlet
hoặc thử cnf xml
trên các nền tảng khác.
xpath
được cài đặt sẵn không phù hợp để sử dụng làm thành phần trong tập lệnh. Xem ví dụ stackoverflow.com/questions/15461737/ cấp để biết thêm chi tiết.
apt-get install xmlstarlet
Điều này là đủ...
xpath xhtmlfile.xhtml '/html/head/title/text()' > titleOfXHTMLPage.txt
apt-get install libxml-xpath-perl
.
Kiểm tra XML2 từ http://www.ofb.net/~egnor/xml2/ để chuyển đổi XML thành định dạng hướng dòng.
bắt đầu từ câu trả lời của chad, đây là giải pháp làm việc HOÀN TOÀN để phân tích UML, với xử lý bình luận propper, chỉ với 2 chức năng nhỏ (hơn 2 bu bạn có thể trộn tất cả chúng). Tôi không nói rằng một trong số đó không hoạt động, nhưng nó có quá nhiều vấn đề với các tệp XML được định dạng kém: Vì vậy, bạn phải khó khăn hơn một chút để xử lý các bình luận và đặt sai chỗ / CR / TAB / v.v.
Mục đích của câu trả lời này là cung cấp sẵn sàng 2 chức năng bash cho bất kỳ ai cần phân tích cú pháp UML mà không cần các công cụ phức tạp sử dụng perl, python hoặc bất cứ thứ gì khác. Đối với tôi, tôi không thể cài đặt cpan, cũng như các mô-đun perl cho HĐH sản xuất cũ mà tôi đang làm việc và python không khả dụng.
Đầu tiên, một định nghĩa về các từ UML được sử dụng trong bài viết này:
<!-- comment... -->
<tag attribute="value">content...</tag>
EDIT: các chức năng được cập nhật, có xử lý:
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
}
và cái thứ hai:
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
}
và cuối cùng, các hàm rtrim, trim và 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 và bạn sẽ cần một số biến động màu sắc gọn gàng được xác định lúc đầu và cũng được xuất:
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"'
Hoặc bạn biết cách tạo các hàm và tải chúng thông qua FPATH (ksh) hoặc mô phỏng của FPATH (bash)
Nếu không, chỉ cần sao chép / dán mọi thứ trên dòng lệnh.
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
Với các bình luận Chế độ gỡ lỗi (-d) và các thuộc tính được phân tích cú pháp được in ra stderr
./read_xml.sh: line 22: (-1): substring expression < 0
?
[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
Tôi không biết về bất kỳ công cụ phân tích cú pháp XML shell thuần túy nào. Vì vậy, rất có thể bạn sẽ cần một công cụ được viết bằng một ngôn ngữ khác.
Mô-đun XML :: Twig Perl của tôi đi kèm với một công cụ như vậy : xml_grep
, nơi bạn có thể sẽ viết những gì bạn muốn xml_grep -t '/html/head/title' xhtmlfile.xhtml > titleOfXHTMLPage.txt
( -t
tùy chọn cung cấp cho bạn kết quả dưới dạng văn bản thay vì xml)
Một công cụ dòng lệnh khác là Xidel mới của tôi . Nó cũng hỗ trợ XPath 2 và XQuery, trái với xpath / xmlstarlet đã được đề cập.
Tiêu đề có thể được đọc như sau:
xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt
Và nó cũng có một tính năng thú vị để xuất nhiều biến sang bash. Ví dụ
eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )
thiết lập $title
tiêu đề và $imgcount
số lượng hình ảnh trong tệp, nên linh hoạt như phân tích cú pháp trực tiếp trong bash.
Sau một số nghiên cứu để dịch giữa các định dạng Linux và Windows của các đường dẫn tệp trong tệp XML, tôi đã tìm thấy các hướng dẫn và giải pháp thú vị về:
Mặc dù có khá nhiều tiện ích bảng điều khiển được làm sẵn có thể làm những gì bạn muốn, nhưng có lẽ sẽ mất ít thời gian hơn để viết một vài dòng mã bằng ngôn ngữ lập trình đa năng như Python mà bạn có thể dễ dàng mở rộng và thích nghi với bạn cần.
Đây là một kịch bản python sử dụng lxml
để phân tích cú pháp - nó lấy tên của tệp hoặc URL làm tham số đầu tiên, biểu thức XPath làm tham số thứ hai và in các chuỗi / nút khớp với biểu thức đã cho.
#!/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
có thể được cài đặt với pip install lxml
. Trên Ubuntu bạn có thể sử dụng sudo apt install python-lxml
.
python xpath.py myfile.xml "//mynode"
lxml
cũng chấp nhận một URL làm đầu vào:
python xpath.py http://www.feedforall.com/sample.xml "//link"
Lưu ý : Nếu XML của bạn có không gian tên mặc định không có tiền tố (ví dụ
xmlns=http://abc...
) thì bạn phải sử dụngp
tiền tố (được cung cấp bởi 'hack') trong các biểu thức của bạn, ví dụ://p:module
để lấy các mô-đun từ mộtpom.xml
tệp. Trong trường hợpp
tiền tố đã được ánh xạ trong XML của bạn, thì bạn sẽ cần sửa đổi tập lệnh để sử dụng tiền tố khác.
Một tập lệnh một lần phục vụ mục đích hẹp là trích xuất tên mô-đun từ tệp maven apache. Lưu ý cách tên nút ( module
) được tiền tố với không gian tên mặc định {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
hơn apt-get
hoặc yum
cuộc gọi. Cảm ơn!
Phương pháp của Yuzem có thể được cải thiện bằng cách đảo ngược thứ tự <
và >
các dấu hiệu trong rdom
hàm và các phép gán biến, sao cho:
rdom () { local IFS=\> ; read -d \< E C ;}
trở thành:
rdom () { local IFS=\< ; read -d \> C E ;}
Nếu việc phân tích cú pháp không được thực hiện như thế này, thẻ cuối cùng trong tệp XML sẽ không bao giờ đạt được. Điều này có thể có vấn đề nếu bạn định xuất một tệp XML khác ở cuối while
vòng lặp.
Điều này hoạt động nếu bạn muốn các thuộc tính 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
Mặc dù có vẻ như "không bao giờ phân tích cú pháp XML, JSON ... từ bash mà không có công cụ thích hợp" là lời khuyên âm thanh, tôi không đồng ý. Nếu đây là công việc phụ, thật tuyệt vời khi tìm kiếm công cụ thích hợp, sau đó học nó ... Awk có thể làm điều đó trong vài phút. Các chương trình của tôi phải làm việc trên tất cả các loại dữ liệu được đề cập ở trên và nhiều loại dữ liệu hơn. Chết tiệt, tôi không muốn thử nghiệm 30 công cụ để phân tích 5-7-10 định dạng khác nhau mà tôi cần nếu tôi có thể đánh thức vấn đề trong vài phút. Tôi không quan tâm đến XML, JSON hay bất cứ điều gì! Tôi cần một giải pháp duy nhất cho tất cả chúng.
Ví dụ: chương trình SmartHome của tôi điều hành nhà của chúng tôi. Trong khi thực hiện, nó đọc rất nhiều dữ liệu ở quá nhiều định dạng khác nhau mà tôi không thể kiểm soát. Tôi không bao giờ sử dụng các công cụ phù hợp, chuyên dụng vì tôi không muốn dành nhiều phút hơn để đọc dữ liệu tôi cần. Với các điều chỉnh FS và RS, giải pháp awk này hoạt động hoàn hảo cho mọi định dạng văn bản. Nhưng, nó có thể không phải là câu trả lời thích hợp khi nhiệm vụ chính của bạn là làm việc chủ yếu với vô số dữ liệu ở định dạng đó!
Vấn đề phân tích cú pháp XML từ bash tôi gặp phải ngày hôm qua. Đây là cách tôi làm điều đó cho bất kỳ định dạng dữ liệu phân cấp. Như một phần thưởng - Tôi gán dữ liệu trực tiếp cho các biến trong tập lệnh bash.
Để làm cho nó dễ đọc hơn, tôi sẽ trình bày giải pháp theo từng giai đoạn. Từ dữ liệu thử nghiệm OP, tôi đã tạo một tệp: test.xml
Phân tích cú pháp XML nói bash và trích xuất dữ liệu trong 90 ký tự:
awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml
Tôi thường sử dụng phiên bản dễ đọc hơn vì nó dễ sửa đổi hơn trong cuộc sống thực vì tôi thường cần phải kiểm tra khác nhau:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml
Tôi không quan tâm định dạng được gọi như thế nào. Tôi chỉ tìm kiếm giải pháp đơn giản nhất. Trong trường hợp cụ thể này, tôi có thể thấy từ dữ liệu rằng dòng mới là dấu tách bản ghi (RS) và <> trường phân định (FS). Trong trường hợp ban đầu của tôi, tôi đã lập chỉ mục phức tạp 6 giá trị trong hai bản ghi, liên quan đến chúng, tìm khi dữ liệu tồn tại cộng với các trường (bản ghi) có thể tồn tại hoặc không tồn tại. Phải mất 4 dòng awk để giải quyết vấn đề một cách hoàn hảo. Vì vậy, hãy thích nghi với từng nhu cầu trước khi sử dụng nó!
Phần thứ hai chỉ đơn giản là nó có chuỗi mong muốn trong một dòng (RS) và nếu vậy, in ra các trường cần thiết (FS). Ở trên tôi mất khoảng 30 giây để sao chép và điều chỉnh từ lệnh cuối cùng mà tôi đã sử dụng theo cách này (dài hơn 4 lần). Và đó là nó! Thực hiện trong 90 ký tự.
Nhưng, tôi luôn cần lấy dữ liệu gọn gàng thành các biến trong tập lệnh của mình. Trước tiên tôi kiểm tra các cấu trúc như vậy:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml
Trong một số trường hợp tôi sử dụng printf thay vì in. Khi tôi thấy mọi thứ có vẻ tốt, tôi chỉ cần hoàn thành việc gán giá trị cho các biến. Tôi biết nhiều người nghĩ "eval" là "ác", không cần bình luận :) Thủ thuật hoạt động hoàn hảo trên cả bốn mạng của tôi trong nhiều năm. Nhưng hãy tiếp tục học nếu bạn không hiểu tại sao điều này có thể là thực hành tồi! Bao gồm các phép gán biến bash và khoảng cách rộng rãi, giải pháp của tôi cần 120 ký tự để làm mọi thứ.
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"