Im Idealfall möchte ich Folgendes tun können:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Im Idealfall möchte ich Folgendes tun können:
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Antworten:
Dies ist wirklich nur eine Erklärung von Yuzems Antwort, aber ich hatte nicht das Gefühl, dass so viel an jemand anderem bearbeitet werden sollte, und Kommentare erlauben keine Formatierung, also ...
rdom () { local IFS=\> ; read -d \< E C ;}
Nennen wir das "read_dom" anstelle von "rdom", platzieren Sie es ein wenig und verwenden Sie längere Variablen:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
}
Okay, es definiert eine Funktion namens read_dom. In der ersten Zeile wird IFS (das Eingabefeldtrennzeichen) für diese Funktion lokalisiert und in> geändert. Das heißt, wenn Sie Daten lesen, anstatt sie automatisch auf Leerzeichen, Tabulatoren oder Zeilenumbrüche aufzuteilen, werden sie auf '>' aufgeteilt. In der nächsten Zeile wird angegeben, dass Eingaben von stdin gelesen werden sollen. Anstatt an einer neuen Zeile anzuhalten, halten Sie an, wenn Sie ein '<' (das -d für Deliminator-Flag) sehen. Was gelesen wird, wird dann mit dem IFS aufgeteilt und der Variablen ENTITY und CONTENT zugewiesen. Nehmen Sie also Folgendes:
<tag>value</tag>
Der erste Aufruf, read_dom
um eine leere Zeichenfolge zu erhalten (da das '<' das erste Zeichen ist). Das wird von IFS in nur '' aufgeteilt, da es kein '>' Zeichen gibt. Read weist dann beiden Variablen eine leere Zeichenfolge zu. Der zweite Aufruf erhält die Zeichenfolge 'tag> value'. Das wird dann vom IFS in die beiden Felder 'tag' und 'value' aufgeteilt. Read weist dann die Variablen wie folgt zu: ENTITY=tag
und CONTENT=value
. Der dritte Aufruf erhält die Zeichenfolge '/ tag>'. Das wird vom IFS in die beiden Felder '/ tag' und '' aufgeteilt. Read weist dann die Variablen wie folgt zu: ENTITY=/tag
und CONTENT=
. Der vierte Aufruf gibt einen Status ungleich Null zurück, da das Dateiende erreicht ist.
Jetzt hat seine while-Schleife ein wenig aufgeräumt, um dem oben genannten zu entsprechen:
while read_dom; do
if [[ $ENTITY = "title" ]]; then
echo $CONTENT
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
In der ersten Zeile steht nur: "Während die Funktion read_dom den Status Null zurückgibt, gehen Sie wie folgt vor." In der zweiten Zeile wird überprüft, ob die Entität, die wir gerade gesehen haben, "Titel" ist. In der nächsten Zeile wird der Inhalt des Tags wiedergegeben. Die vier Zeilen werden beendet. Wenn es nicht die Titelentität war, wird die Schleife in der sechsten Zeile wiederholt. Wir leiten "xhtmlfile.xhtml" in die Standardeingabe (für die read_dom
Funktion) und die Standardausgabe in "titleOfXHTMLPage.txt" (das Echo von früher in der Schleife) um.
Geben Sie nun Folgendes an (ähnlich wie beim Auflisten eines Buckets in S3) für 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>
und die folgende Schleife:
while read_dom; do
echo "$ENTITY => $CONTENT"
done < input.xml
Du solltest bekommen:
=>
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 =>
Wenn wir also eine while
Schleife wie die von Yuzem geschrieben haben:
while read_dom; do
if [[ $ENTITY = "Key" ]] ; then
echo $CONTENT
fi
done < input.xml
Wir würden eine Liste aller Dateien im S3-Bucket erhalten.
BEARBEITEN
Wenn es aus irgendeinem Grund local IFS=\>
bei Ihnen nicht funktioniert und Sie es global festlegen, sollten Sie es am Ende der Funktion wie folgt zurücksetzen:
read_dom () {
ORIGINAL_IFS=$IFS
IFS=\>
read -d \< ENTITY CONTENT
IFS=$ORIGINAL_IFS
}
Andernfalls wird jede Zeilenaufteilung, die Sie später im Skript vornehmen, durcheinander gebracht.
BEARBEITEN 2
Um Attributname / Wert-Paare aufzuteilen, können Sie Folgendes erweitern read_dom()
:
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local ret=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $ret
}
Schreiben Sie dann Ihre Funktion, um die gewünschten Daten zu analysieren und abzurufen:
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
}
Dann, während Sie read_dom
anrufen parse_dom
:
while read_dom; do
parse_dom
done
Geben Sie dann das folgende Beispiel-Markup an:
<example>
<bar size="bar_size" type="metal">bars content</bar>
<foo size="1789" type="unknown">foos content</foo>
</example>
Sie sollten diese Ausgabe erhalten:
$ cat example.xml | ./bash_xml.sh
bar type is: metal
foo size is: 1789
EDIT 3 Ein anderer Benutzer gab an, Probleme mit FreeBSD zu haben, und schlug vor, den Exit-Status vor dem Lesen zu speichern und am Ende von read_dom zurückzugeben.
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local RET=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $RET
}
Ich sehe keinen Grund, warum das nicht funktionieren sollte
IFS=\< read ...
, wodurch nur IFS für den Leseaufruf festgelegt wird. (Beachten Sie, dass ich die Praxis des read
Parsen von XML in keiner Weise befürworte , und ich glaube, dass dies mit Gefahren behaftet ist und vermieden werden sollte.)
Sie können dies sehr einfach mit nur Bash tun. Sie müssen nur diese Funktion hinzufügen:
rdom () { local IFS=\> ; read -d \< E C ;}
Jetzt können Sie rdom wie read verwenden, aber für HTML-Dokumente. Beim Aufruf von rdom wird das Element der Variablen E und der Inhalt der Variable C zugewiesen.
Zum Beispiel, um das zu tun, was Sie tun wollten:
while rdom; do
if [[ $E = title ]]; then
echo $C
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
Zu den Befehlszeilentools, die über Shell-Skripte aufgerufen werden können, gehören:
Ich verwende auch xmllint und xsltproc mit kleinen XSL-Transformationsskripten, um die XML-Verarbeitung über die Befehlszeile oder in Shell-Skripten durchzuführen.
Sie können das Dienstprogramm xpath verwenden. Es wird mit dem Perl XML-XPath-Paket installiert.
Verwendung:
/usr/bin/xpath [filename] query
oder XMLStarlet . Um es auf opensuse zu installieren, verwenden Sie:
sudo zypper install xmlstarlet
oder versuchen Sie es cnf xml
auf anderen Plattformen.
xpath
vorinstallierte Version nicht für die Verwendung als Komponente in Skripten geeignet. Siehe z. B. stackoverflow.com/questions/15461737/… für eine Ausarbeitung.
apt-get install xmlstarlet
Das ist ausreichend ...
xpath xhtmlfile.xhtml '/html/head/title/text()' > titleOfXHTMLPage.txt
apt-get install libxml-xpath-perl
.
Schauen Sie sich XML2 unter http://www.ofb.net/~egnor/xml2/ an , das XML in ein zeilenorientiertes Format konvertiert.
Ausgehend von der Antwort des Chad ist hier die KOMPLETTE Arbeitslösung zum Parsen von UML mit korrekter Behandlung von Kommentaren mit nur 2 kleinen Funktionen (mehr als 2 bu können Sie alle mischen). Ich sage nicht, dass chads überhaupt nicht funktioniert hat, aber es gab zu viele Probleme mit schlecht formatierten XML-Dateien: Sie müssen also etwas kniffliger sein, um mit Kommentaren und falsch platzierten Leerzeichen / CR / TAB / etc. Zu umgehen.
Der Zweck dieser Antwort besteht darin, jedem, der UML ohne komplexe Tools mit Perl, Python oder etwas anderem analysieren muss, sofort einsatzbereite Bash-Funktionen zur Verfügung zu stellen. Ich kann weder cpan noch Perl-Module für das alte Produktionsbetriebssystem installieren, an dem ich arbeite, und Python ist nicht verfügbar.
Zunächst eine Definition der in diesem Beitrag verwendeten UML-Wörter:
<!-- comment... -->
<tag attribute="value">content...</tag>
EDIT: aktualisierte Funktionen, mit Handle von:
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
}
und der zweite:
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
}
und schließlich die Funktionen rtrim, trim und 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, und Sie müssen zunächst einige ordentliche kolorierende dynamische Variablen definieren und auch exportieren:
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"'
Entweder wissen Sie, wie Sie Funktionen erstellen und über FPATH (ksh) oder eine Emulation von FPATH (bash) laden.
Wenn nicht, kopieren Sie einfach alles in die Befehlszeile.
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
Im Debug-Modus (-d) werden Kommentare und analysierte Attribute auf stderr gedruckt
./read_xml.sh: line 22: (-1): substring expression < 0
?
[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
Mir ist kein reines Shell-XML-Parsing-Tool bekannt. Sie benötigen also höchstwahrscheinlich ein Tool, das in einer anderen Sprache geschrieben ist.
Mein XML :: Twig Perl-Modul enthält ein solches Tool: xml_grep
Hier können Sie wahrscheinlich schreiben, als was Sie möchten xml_grep -t '/html/head/title' xhtmlfile.xhtml > titleOfXHTMLPage.txt
(die -t
Option gibt Ihnen das Ergebnis als Text anstelle von XML).
Ein weiteres Kommandozeilen-Tool ist mein neues Xidel . Es unterstützt auch XPath 2 und XQuery, im Gegensatz zu dem bereits erwähnten xpath / xmlstarlet.
Der Titel kann wie folgt gelesen werden:
xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt
Und es hat auch eine coole Funktion, um mehrere Variablen nach Bash zu exportieren. Beispielsweise
eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )
Legt $title
den Titel und $imgcount
die Anzahl der Bilder in der Datei fest, die so flexibel sein sollten wie das direkte Parsen in Bash.
Nach einigen Recherchen zur Übersetzung der Dateipfade in XML-Dateien zwischen Linux- und Windows-Formaten fand ich interessante Tutorials und Lösungen zu folgenden Themen:
Zwar gibt es einige vorgefertigte Konsolendienstprogramme, die möglicherweise das tun, was Sie möchten, aber es wird wahrscheinlich weniger Zeit in Anspruch nehmen, einige Codezeilen in einer universellen Programmiersprache wie Python zu schreiben, die Sie problemlos erweitern und anpassen können Deine Bedürfnisse.
Hier ist ein Python-Skript, das lxml
zum Parsen verwendet wird: Es verwendet den Namen einer Datei oder einer URL als ersten Parameter, einen XPath-Ausdruck als zweiten Parameter und druckt die Zeichenfolgen / Knoten, die dem angegebenen Ausdruck entsprechen.
#!/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
kann mit installiert werden pip install lxml
. Auf Ubuntu können Sie verwenden sudo apt install python-lxml
.
python xpath.py myfile.xml "//mynode"
lxml
akzeptiert auch eine URL als Eingabe:
python xpath.py http://www.feedforall.com/sample.xml "//link"
Hinweis : Wenn Ihr XML einen Standard-Namespace ohne Präfix hat (z. B.
xmlns=http://abc...
), müssen Sie dasp
Präfix (bereitgestellt durch den 'Hack') in Ihren Ausdrücken verwenden, z. B.//p:module
um die Module aus einerpom.xml
Datei abzurufen. Falls dasp
Präfix bereits in Ihrem XML zugeordnet ist, müssen Sie das Skript ändern, um ein anderes Präfix zu verwenden.
Ein einmaliges Skript, das dem engen Zweck dient, Modulnamen aus einer Apache-Maven-Datei zu extrahieren. Beachten Sie, wie dem Knotennamen ( module
) der Standard-Namespace vorangestellt wird {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
oder einen yum
Call rechtfertigen . Vielen Dank!
Die Methode von Yuzem kann verbessert werden, indem die Reihenfolge der <
und >
-Zeichen in der rdom
Funktion und die Variablenzuweisungen umgekehrt werden , so dass:
rdom () { local IFS=\> ; read -d \< E C ;}
wird:
rdom () { local IFS=\< ; read -d \> C E ;}
Wenn das Parsen nicht so durchgeführt wird, wird das letzte Tag in der XML-Datei nie erreicht. Dies kann problematisch sein, wenn Sie am Ende der while
Schleife eine weitere XML-Datei ausgeben möchten.
Dies funktioniert, wenn Sie XML-Attribute wünschen:
$ 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
Obwohl es so aussieht, als ob "niemals XML, JSON ... von Bash ohne ein geeignetes Tool analysieren" ein guter Rat ist, bin ich anderer Meinung. Wenn dies ein Nebenjob ist, ist es schwierig, nach dem richtigen Werkzeug zu suchen und es dann zu lernen ... Awk kann es in Minuten erledigen. Meine Programme müssen mit allen oben genannten und mehr Arten von Daten arbeiten. Zur Hölle, ich möchte nicht 30 Tools testen, um 5-7-10 verschiedene Formate zu analysieren, die ich brauche, wenn ich das Problem in Minuten lösen kann. XML, JSON oder was auch immer interessieren mich nicht! Ich brauche eine einzige Lösung für alle.
Als Beispiel: Mein SmartHome-Programm führt unsere Häuser. Dabei liest es eine Vielzahl von Daten in zu vielen verschiedenen Formaten, die ich nicht kontrollieren kann. Ich verwende niemals dedizierte, geeignete Tools, da ich nicht mehr als Minuten mit dem Lesen der benötigten Daten verbringen möchte. Mit FS- und RS-Anpassungen funktioniert diese awk-Lösung perfekt für jedes Textformat. Es ist jedoch möglicherweise nicht die richtige Antwort, wenn Ihre Hauptaufgabe darin besteht, hauptsächlich mit vielen Daten in diesem Format zu arbeiten!
Das Problem, XML von Bash zu analysieren, hatte ich gestern. So mache ich das für jedes hierarchische Datenformat. Als Bonus - Ich ordne den Variablen in einem Bash-Skript Daten direkt zu.
Um die Lesbarkeit zu verbessern, werde ich die Lösung schrittweise vorstellen. Aus den OP-Testdaten habe ich eine Datei erstellt: test.xml
Analysieren Sie das XML in Bash und extrahieren Sie die Daten in 90 Zeichen:
awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml
Normalerweise verwende ich eine besser lesbare Version, da es im wirklichen Leben einfacher ist, Änderungen vorzunehmen, da ich häufig anders testen muss:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml
Es ist mir egal, wie das Format heißt. Ich suche nur die einfachste Lösung. In diesem speziellen Fall kann ich anhand der Daten erkennen, dass Newline das Datensatztrennzeichen (RS) und das <> Begrenzungsfeld (FS) ist. In meinem ursprünglichen Fall hatte ich eine komplizierte Indizierung von 6 Werten innerhalb von zwei Datensätzen, um sie zu verknüpfen und festzustellen, wann die Daten vorhanden sind, plus Felder (Datensätze), die möglicherweise vorhanden sind oder nicht. Es dauerte 4 Zeilen awk, um das Problem perfekt zu lösen. Passen Sie die Idee also an jeden Bedarf an, bevor Sie sie verwenden!
Der zweite Teil sieht einfach so aus, als ob eine Zeichenfolge in einer Zeile (RS) gesucht wird, und wenn ja, werden die erforderlichen Felder (FS) ausgedruckt. Ich habe ungefähr 30 Sekunden gebraucht, um den letzten Befehl, den ich auf diese Weise verwendet habe, zu kopieren und anzupassen (viermal länger). Und das ist alles! Fertig in 90 Zeichen.
Aber ich muss die Daten in meinem Skript immer ordentlich in Variablen umwandeln. Ich teste die Konstrukte zuerst so:
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml
In einigen Fällen verwende ich printf anstelle von print. Wenn ich sehe, dass alles gut aussieht, beende ich einfach die Zuweisung von Werten zu Variablen. Ich weiß, dass viele denken, "eval" sei "böse", keine Notwendigkeit zu kommentieren :) Trick funktioniert seit Jahren perfekt in allen vier meiner Netzwerke. Aber lernen Sie weiter, wenn Sie nicht verstehen, warum dies eine schlechte Praxis sein kann! Inklusive Bash-Variablenzuweisungen und großem Abstand benötigt meine Lösung 120 Zeichen, um alles zu erledigen.
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"