Idealnie, chciałbym móc:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Idealnie, chciałbym móc:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Odpowiedzi:
To naprawdę tylko wyjaśnienie odpowiedzi Yuzem , ale nie sądziłem, że tak wiele edycji powinno się robić komuś innemu, a komentarze nie pozwalają na formatowanie, więc ...
rdom () { local IFS=\> ; read -d \< E C ;}
Nazwijmy to „read_dom” zamiast „rdom”, oddzielmy to trochę i używaj dłuższych zmiennych:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
}
OK, więc definiuje funkcję o nazwie read_dom. Pierwsza linia ustawia IFS (separator pól wejściowych) jako lokalny dla tej funkcji i zmienia go na>. Oznacza to, że kiedy czytasz dane, zamiast automatycznie dzielić je na spację, tabulację lub znaki nowej linii, są one dzielone na „>”. Następna linia mówi, że należy czytać dane wejściowe ze standardowego wejścia i zamiast zatrzymywać się na nowej linii, zatrzymaj się, gdy zobaczysz znak „<” (-d dla flagi separatora). To, co jest odczytywane, jest następnie dzielone za pomocą IFS i przypisywane do zmiennej ENTITY i CONTENT. Więc weź następujące:
<tag>value</tag>
Pierwsze wywołanie w celu read_dom
uzyskania pustego ciągu (ponieważ „<” jest pierwszym znakiem). To zostaje podzielone przez IFS na po prostu „”, ponieważ nie ma znaku „>”. Read następnie przypisuje pusty ciąg do obu zmiennych. Drugie wywołanie pobiera ciąg „tag> wartość”. To zostaje następnie podzielone przez IFS na dwa pola „tag” i „wartość”. Read następnie przypisuje zmienne, takie jak: ENTITY=tag
i CONTENT=value
. Trzecie wywołanie otrzymuje ciąg „/ tag>”. To zostaje podzielone przez IFS na dwa pola „/ tag” i „”. Read następnie przypisuje zmienne, takie jak: ENTITY=/tag
i CONTENT=
. Czwarte wywołanie zwróci stan niezerowy, ponieważ dotarliśmy do końca pliku.
Teraz jego pętla while została nieco wyczyszczona, aby pasowała do powyższego:
while read_dom; do
if [[ $ENTITY = "title" ]]; then
echo $CONTENT
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Pierwsza linia mówi po prostu, „podczas gdy funkcja read_dom zwraca stan zerowy, wykonaj następujące czynności”. Druga linia sprawdza, czy jednostka, którą właśnie widzieliśmy, to „tytuł”. W następnym wierszu zostanie wyświetlona treść tagu. Cztery linie wychodzą. Jeśli nie był to tytuł, pętla powtarza się w szóstym wierszu. Przekierowujemy „xhtmlfile.xhtml” na standardowe wejście (dla read_dom
funkcji) i przekierowujemy standardowe wyjście do „titleOfXHTMLPage.txt” (echo z wcześniejszej części pętli).
Teraz biorąc pod uwagę następujące informacje (podobne do tego, co otrzymujesz z umieszczenia wiadra na S3) dla 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>
i następującą pętlę:
while read_dom; do
echo "$ENTITY => $CONTENT"
done < input.xml
Powinieneś wziąć:
=>
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 =>
Jeśli więc napisaliśmy while
pętlę taką jak Yuzem:
while read_dom; do
if [[ $ENTITY = "Key" ]] ; then
echo $CONTENT
fi
done < input.xml
Otrzymalibyśmy listę wszystkich plików w zasobniku S3.
EDYCJA
Jeśli z jakiegoś powodu local IFS=\>
nie działa u Ciebie i ustawiasz ją globalnie, powinieneś zresetować ją na końcu funkcji, np .:
read_dom () {
ORIGINAL_IFS=$IFS
IFS=\>
read -d \< ENTITY CONTENT
IFS=$ORIGINAL_IFS
}
W przeciwnym razie każdy podział linii, który wykonasz później w skrypcie, będzie pomieszany.
EDYCJA 2
Aby rozdzielić pary nazwa / wartość atrybutu, możesz rozszerzyć w ten read_dom()
sposób:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local ret=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $ret
}
Następnie napisz swoją funkcję, aby przeanalizować i uzyskać dane, które chcesz w ten sposób:
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
}
Następnie podczas read_dom
rozmowy parse_dom
:
while read_dom; do
parse_dom
done
Następnie, biorąc pod uwagę następujący przykładowy znacznik:
<example>
<bar size="bar_size" type="metal">bars content</bar>
<foo size="1789" type="unknown">foos content</foo>
</example>
Powinieneś otrzymać to wyjście:
$ cat example.xml | ./bash_xml.sh
bar type is: metal
foo size is: 1789
EDIT 3 inny użytkownik powiedział, że ma z tym problemy we FreeBSD i zasugerował zapisanie statusu wyjścia z odczytu i zwrócenie go na końcu read_dom, na przykład:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local RET=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $RET
}
Nie widzę powodu, dla którego to nie powinno działać
IFS=\< read ...
co ustawi IFS tylko dla wywołania read. (Zauważ, że w żaden sposób nie popieram praktyki używania read
do parsowania xml i uważam, że jest to najeżone niebezpieczeństwem i powinno się go unikać.)
Możesz to zrobić bardzo łatwo używając samego basha. Musisz tylko dodać tę funkcję:
rdom () { local IFS=\> ; read -d \< E C ;}
Teraz możesz używać rdom jak read, ale dla dokumentów html. Po wywołaniu rdom przypisze element do zmiennej E, a zawartość do zmiennej C.
Na przykład, aby zrobić to, co chciałeś:
while rdom; do
if [[ $E = title ]]; then
echo $C
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Narzędzia wiersza polecenia, które można wywołać ze skryptów powłoki, obejmują:
Używam również xmllint i xsltproc z małymi skryptami transformacji XSL do przetwarzania XML z wiersza poleceń lub w skryptach powłoki.
Możesz użyć narzędzia xpath. Jest instalowany z pakietem Perl XML-XPath.
Stosowanie:
/usr/bin/xpath [filename] query
lub XMLStarlet . Aby zainstalować go na opensuse użyj:
sudo zypper install xmlstarlet
lub wypróbuj cnf xml
na innych platformach.
xpath
preinstalowany element nie nadaje się do użycia jako składnik skryptów. Zobacz np. Stackoverflow.com/questions/15461737/… w celu uzyskania dalszych informacji.
apt-get install xmlstarlet
To wystarczy ...
xpath xhtmlfile.xhtml '/html/head/title/text()' > titleOfXHTMLPage.txt
apt-get install libxml-xpath-perl
.
Sprawdź XML2 z http://www.ofb.net/~egnor/xml2/, który konwertuje XML na format liniowy.
zaczynając od odpowiedzi czada, oto KOMPLETNIE działające rozwiązanie do parsowania UML, z właściwą obsługą komentarzy, z zaledwie 2 małymi funkcjami (więcej niż 2, ale możesz je wszystkie mieszać). Nie mówię, że jeden z Chada w ogóle nie działał, ale miał zbyt wiele problemów ze źle sformatowanymi plikami XML: Więc musisz być nieco trudniejszy, aby radzić sobie z komentarzami i niewłaściwymi spacjami / CR / TAB / itp.
Celem tej odpowiedzi jest udostępnienie gotowych do użycia, gotowych do użycia funkcji bash każdemu, kto potrzebuje analizować UML bez skomplikowanych narzędzi przy użyciu perla, pythona lub czegokolwiek innego. Jeśli chodzi o mnie, nie mogę zainstalować modułów cpan ani perl dla starego produkcyjnego systemu operacyjnego, na którym pracuję, a Python nie jest dostępny.
Najpierw definicja słów UML użytych w tym poście:
<!-- comment... -->
<tag attribute="value">content...</tag>
EDYCJA: zaktualizowane funkcje, z uchwytem:
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
}
a drugi:
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
}
i na koniec funkcje rtrim, trim i 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; }
Och, a będziesz potrzebować kilku zgrabnych, kolorujących zmiennych dynamicznych, które zostaną najpierw zdefiniowane i wyeksportowane:
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"'
Albo wiesz, jak tworzyć funkcje i ładować je przez FPATH (ksh) lub emulację FPATH (bash)
Jeśli nie, po prostu skopiuj / wklej wszystko w wierszu poleceń.
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
W trybie debugowania (-d) komentarze i przeanalizowane atrybuty są wypisywane na stderr
./read_xml.sh: line 22: (-1): substring expression < 0
:?
[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
Nie znam żadnego narzędzia do analizowania czystego XML-a. Najprawdopodobniej będziesz potrzebować narzędzia napisanego w innym języku.
Mój moduł XML :: Twig Perl zawiera takie narzędzie:, xml_grep
gdzie prawdopodobnie napiszesz to, co chcesz xml_grep -t '/html/head/title' xhtmlfile.xhtml > titleOfXHTMLPage.txt
( -t
opcja daje wynik jako tekst zamiast xml)
Kolejnym narzędziem wiersza poleceń jest mój nowy Xidel . Obsługuje również XPath 2 i XQuery, w przeciwieństwie do wspomnianego już xpath / xmlstarlet.
Tytuł można czytać tak:
xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt
Ma też fajną funkcję eksportowania wielu zmiennych do basha. Na przykład
eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )
ustawia $title
tytuł i $imgcount
liczbę obrazów w pliku, co powinno być tak elastyczne, jak przetwarzanie go bezpośrednio w bashu.
Po poszukiwaniach tłumaczenia między formatami Linux i Windows ścieżek plików w plikach XML znalazłem ciekawe samouczki i rozwiązania dotyczące:
Chociaż istnieje sporo gotowych narzędzi konsolowych, które mogą robić to, co chcesz, prawdopodobnie napisanie kilku wierszy kodu w języku programowania ogólnego przeznaczenia, takim jak Python, zajmie prawdopodobnie mniej czasu, który można łatwo rozszerzyć i dostosować do Twoje potrzeby.
Oto skrypt w Pythonie, który używa lxml
do analizy - przyjmuje nazwę pliku lub adres URL jako pierwszy parametr, wyrażenie XPath jako drugi parametr i drukuje ciągi / węzły pasujące do danego wyrażenia.
#!/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
można zainstalować z pip install lxml
. Na Ubuntu możesz użyć sudo apt install python-lxml
.
python xpath.py myfile.xml "//mynode"
lxml
akceptuje również adres URL jako dane wejściowe:
python xpath.py http://www.feedforall.com/sample.xml "//link"
Uwaga : Jeśli twój XML ma domyślną przestrzeń nazw bez prefiksu (np.
xmlns=http://abc...
), Musisz użyćp
przedrostka (dostarczonego przez 'hack') w swoich wyrażeniach, np.//p:module
Aby pobrać moduły zpom.xml
pliku. W przypadku, gdyp
prefiks jest już odwzorowany w Twoim XML, musisz zmodyfikować skrypt, aby używał innego prefiksu.
Jednorazowy skrypt, który służy wąskim celom wyodrębniania nazw modułów z pliku maven Apache. Zwróć uwagę, jak nazwa węzła ( module
) jest poprzedzona domyślną przestrzenią nazw {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
lub yum
połączenia. Dzięki!
Metodę Yuzema można ulepszyć odwracając kolejność znaków <
i >
w rdom
funkcji oraz przypisania zmiennych, tak aby:
rdom () { local IFS=\> ; read -d \< E C ;}
staje się:
rdom () { local IFS=\< ; read -d \> C E ;}
Jeśli parsowanie nie zostanie wykonane w ten sposób, ostatni znacznik w pliku XML nigdy nie zostanie osiągnięty. Może to być problematyczne, jeśli zamierzasz wyprowadzić inny plik XML na końcu while
pętli.
Działa to, jeśli potrzebujesz atrybutów 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
Chociaż wydaje się, że „nigdy nie parsuj XML, JSON… z basha bez odpowiedniego narzędzia” to rozsądna rada, nie zgadzam się. Jeśli to praca poboczna, warto poszukać odpowiedniego narzędzia, a potem się tego nauczyć ... Awk może to zrobić w kilka minut. Moje programy muszą pracować na wszystkich wyżej wymienionych i na wielu innych rodzajach danych. Do diabła, nie chcę testować 30 narzędzi do analizowania 5-7-10 różnych formatów, których potrzebuję, jeśli mogę rozwiązać problem w ciągu kilku minut. Nie obchodzi mnie XML, JSON czy cokolwiek! Potrzebuję jednego rozwiązania dla wszystkich.
Na przykład: mój program SmartHome obsługuje nasze domy. Robiąc to, czyta mnóstwo danych w zbyt wielu różnych formatach, których nie mogę kontrolować. Nigdy nie używam dedykowanych, odpowiednich narzędzi, ponieważ nie chcę spędzać więcej niż minuty na odczytywaniu potrzebnych mi danych. Dzięki dostosowaniom FS i RS to rozwiązanie awk działa doskonale dla każdego formatu tekstowego. Jednak może to nie być właściwa odpowiedź, gdy Twoim głównym zadaniem jest praca głównie z dużą ilością danych w tym formacie!
Problem z parsowaniem XML z bash napotkałem wczoraj. Oto jak to robię dla dowolnego hierarchicznego formatu danych. Jako bonus - przypisuję dane bezpośrednio do zmiennych w skrypcie basha.
Dla ułatwienia czytania przedstawię rozwiązanie etapami. Z danych testowych OP utworzyłem plik: test.xml
Przetwarzanie wspomnianego XML w bashu i wyodrębnianie danych w 90 znakach:
awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml
Zwykle używam bardziej czytelnej wersji, ponieważ łatwiej jest ją modyfikować w prawdziwym życiu, ponieważ często muszę testować inaczej:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml
Nie obchodzi mnie, jak nazywa się ten format. Szukam tylko najprostszego rozwiązania. W tym konkretnym przypadku widzę z danych, że znak nowej linii jest separatorem rekordów (RS) i <> ogranicznikami pól (FS). W moim pierwotnym przypadku miałem skomplikowane indeksowanie 6 wartości w dwóch rekordach, odnosząc je, znajdując, kiedy dane istnieją oraz pola (rekordy) mogą istnieć lub nie. Idealne rozwiązanie problemu zajęło 4 linijki awk. Dlatego dostosuj pomysł do każdej potrzeby, zanim go użyjesz!
Druga część po prostu wygląda na to, że w linii (RS) jest poszukiwany łańcuch i jeśli tak, wypisuje potrzebne pola (FS). Powyższe zajęło mi około 30 sekund, aby skopiować i dostosować od ostatniego polecenia, którego użyłem w ten sposób (4 razy dłużej). I to jest to! Sporządzono w 90 znakach.
Ale zawsze muszę starannie umieścić dane w zmiennych w moim skrypcie. Najpierw testuję konstrukcje w następujący sposób:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml
W niektórych przypadkach używam printf zamiast print. Kiedy widzę, że wszystko wygląda dobrze, po prostu kończę przypisywanie wartości do zmiennych. Wiem, że wielu uważa, że „eval” jest „zły”, nie ma potrzeby komentowania :) Trick działa doskonale we wszystkich czterech moich sieciach od lat. Ale ucz się dalej, jeśli nie rozumiesz, dlaczego może to być zła praktyka! Uwzględniając przypisania zmiennych bash i duże odstępy, moje rozwiązanie potrzebuje 120 znaków, aby zrobić wszystko.
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"