XML को Bash में पार्स कैसे करें?


134

आदर्श रूप में, मैं जो करना चाहूंगा वह है:

cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt

जवाबों:


153

यह वास्तव में केवल युजेम के उत्तर का स्पष्टीकरण है , लेकिन मुझे ऐसा नहीं लगा कि यह संपादन किसी और को किया जाना चाहिए, और टिप्पणियां प्रारूपण की अनुमति नहीं देती हैं, इसलिए ...

rdom () { local IFS=\> ; read -d \< E C ;}

चलो "rdom" के बजाय उस "read_dom" को कॉल करते हैं, इसे थोड़ा बाहर निकालते हैं और लंबे चर का उपयोग करते हैं:

read_dom () {
    local IFS=\>
    read -d \< ENTITY CONTENT
}

ठीक है, इसलिए यह read_dom नामक फ़ंक्शन को परिभाषित करता है। पहली पंक्ति IFS (इनपुट फ़ील्ड विभाजक) को इस फ़ंक्शन के लिए स्थानीय बनाती है और इसे> में बदल देती है। इसका मतलब है कि जब आप स्पेस, टैब या न्यूलाइन्स पर स्वचालित रूप से विभाजित होने के बजाय डेटा पढ़ते हैं तो यह '>' पर विभाजित हो जाता है। अगली पंक्ति स्टड से इनपुट पढ़ने के लिए कहती है, और एक नई रेखा पर रुकने के बजाय, जब आप '<' अक्षर (डेलिमिनेटर फ्लैग के लिए -d) देखते हैं तो रुक जाएं। फिर जो पढ़ा जाता है, वह IFS का उपयोग करके विभाजित होता है और चर ENTITY और CONTENT को सौंपा जाता है। तो निम्नलिखित लें:

<tag>value</tag>

read_domखाली स्ट्रिंग पाने के लिए पहला कॉल ('<' पहला पात्र है)। यह आईएफएस द्वारा सिर्फ '' में विभाजित हो जाता है, क्योंकि कोई '>' वर्ण नहीं है। पढ़ें तो दोनों चर के लिए एक खाली स्ट्रिंग प्रदान करता है। दूसरी कॉल को स्ट्रिंग 'टैग> मान' मिलता है। फिर IFS द्वारा दो क्षेत्रों 'टैग' और 'मूल्य' में विभाजित हो जाता है। पढ़ें तो जैसे चर प्रदान करता है: ENTITY=tagऔर CONTENT=value। तीसरी कॉल को स्ट्रिंग '/ टैग>' मिलता है। यह IFS द्वारा दो क्षेत्रों '/ टैग' और '' में विभाजित हो जाता है। पढ़ें तो जैसे चर प्रदान करता है: ENTITY=/tagऔर CONTENT=। चौथी कॉल गैर-शून्य स्थिति लौटाएगी क्योंकि हम फ़ाइल के अंत तक पहुँच चुके हैं।

अब जबकि उसके लूप ने ऊपर से मिलान करने के लिए थोड़ी सफाई की:

while read_dom; do
    if [[ $ENTITY = "title" ]]; then
        echo $CONTENT
        exit
    fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt

पहली पंक्ति बस यही कहती है, "जबकि read_dom शून्य स्थिति का कार्य करता है, निम्न कार्य करें।" दूसरी पंक्ति यह जाँचती है कि जिस इकाई को हमने अभी देखा है वह "शीर्षक" है। अगली पंक्ति टैग की सामग्री को प्रतिध्वनित करती है। चार लाइन से बाहर निकलता है। यदि यह शीर्षक इकाई नहीं थी, तो लूप छठी पंक्ति में दोहराता है। हम मानक इनपुट ( read_domफ़ंक्शन के लिए) में "xhtmlfile.xhtml" को रीडायरेक्ट करते हैं और मानक आउटपुट को "titleOfXHTMLPage.txt" (पहले से लूप में गूंज) में रीडायरेक्ट करते हैं।

अब निम्नलिखित दिए गए हैं (S3 पर बाल्टी सूचीबद्ध करने से आपको जो मिलता है) 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>&quot;0032a28286680abee71aed5d059c6a09&quot;</ETag>
    <Size>1785</Size>
    <StorageClass>STANDARD</StorageClass>
  </Contents>
</ListBucketResult>

और निम्नलिखित लूप:

while read_dom; do
    echo "$ENTITY => $CONTENT"
done < 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 => &quot;0032a28286680abee71aed5d059c6a09&quot;
/ETag => 
Size => 1785
/Size => 
StorageClass => STANDARD
/StorageClass => 
/Contents => 

तो अगर हम whileYuzem की तरह एक पाश लिखा है :

while read_dom; do
    if [[ $ENTITY = "Key" ]] ; then
        echo $CONTENT
    fi
done < input.xml

हमें S3 बाल्टी में सभी फाइलों की एक सूची मिलेगी।

संपादित करें यदि किसी कारण से local IFS=\>आपके लिए काम नहीं करता है और आपने इसे विश्व स्तर पर सेट किया है, तो आपको इसे फ़ंक्शन के अंत में रीसेट करना चाहिए जैसे:

read_dom () {
    ORIGINAL_IFS=$IFS
    IFS=\>
    read -d \< ENTITY CONTENT
    IFS=$ORIGINAL_IFS
}

अन्यथा, स्क्रिप्ट में बाद में आप जो भी विभाजन करते हैं, वह गड़बड़ हो जाएगा।

EDIT 2 विशेषता नाम / मूल्य जोड़े को विभाजित करने के लिए आप इस read_dom()तरह से बढ़ा सकते हैं :

read_dom () {
    local IFS=\>
    read -d \< ENTITY CONTENT
    local ret=$?
    TAG_NAME=${ENTITY%% *}
    ATTRIBUTES=${ENTITY#* }
    return $ret
}

फिर अपने फ़ंक्शन को पार्स करने के लिए लिखें और उस डेटा को प्राप्त करें जो आप इस तरह चाहते हैं:

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
}

फिर जब आप read_domफोन करते हैं parse_dom:

while read_dom; do
    parse_dom
done

फिर निम्न उदाहरण मार्कअप दिया गया:

<example>
  <bar size="bar_size" type="metal">bars content</bar>
  <foo size="1789" type="unknown">foos content</foo>
</example>

आपको यह आउटपुट मिलना चाहिए:

$ cat example.xml | ./bash_xml.sh 
bar type is: metal
foo size is: 1789

EDIT 3 एक अन्य उपयोगकर्ता ने कहा कि उन्हें FreeBSD में समस्या हो रही थी और सुझाव दिया कि बाहर निकलने की स्थिति को पढ़ने से बचाएं और इसे read_dom के अंत में लौटा दें:

read_dom () {
    local IFS=\>
    read -d \< ENTITY CONTENT
    local RET=$?
    TAG_NAME=${ENTITY%% *}
    ATTRIBUTES=${ENTITY#* }
    return $RET
}

मुझे कोई कारण नहीं दिख रहा है कि काम क्यों नहीं करना चाहिए


2
यदि आप IFS (इनपुट फील्ड सेपरेटर) को वैश्विक बनाते हैं तो आपको इसे अपने मूल मूल्य पर वापस सेट कर देना चाहिए, मैंने इसका जवाब दिया। अन्यथा आपके द्वारा अपनी स्क्रिप्ट में बाद में किए गए किसी भी अन्य इनपुट को विभाजित किया जाएगा। मुझे संदेह है कि स्थानीय कारण आपके लिए काम नहीं करता है क्योंकि या तो आप संगतता मोड में बैश का उपयोग कर रहे हैं (जैसे कि आपका शबंग #! / बिन / श) है या यह बैश का प्राचीन संस्करण है।
चाड

30
सिर्फ इसलिए कि आप अपने खुद के पार्सर लिख सकते हैं, इसका मतलब यह नहीं है कि आपको चाहिए।
स्टीफन नेदिज़ेल्स्की

1
@chad यह निश्चित रूप से एडब्ल्यूएस 'कार्यप्रवाह / कार्यान्वयन के बारे में कुछ है कि मैं भी करने के लिए "बैश xml" का उत्तर खोज रहा था कहते हैं wget एक S3 बाल्टी की सामग्री!
एलेस्टेयर

2
@Alair bith लिपियों के एक सेट के लिए github.com/chad3814/s3scripts देखें जिसे हम S3 ऑब्जेक्ट्स में हेरफेर करने के लिए उपयोग करते हैं
chad

5
एक स्थानीय चर में IFS असाइन करना नाजुक है और आवश्यक नहीं है। बस करो: IFS=\< read ...जो केवल रीड कॉल के लिए IFS सेट करेगा। (ध्यान दें कि मैं किसी भी तरह readसे पार्स एक्सएमएल का उपयोग करने के अभ्यास का समर्थन नहीं कर रहा हूं, और मेरा मानना ​​है कि ऐसा करना जोखिम से भरा हुआ है और इससे बचा जाना चाहिए।)
विलियम पर्ससेल

64

आप बहुत ही आसानी से केवल बैश का उपयोग कर सकते हैं। आपको केवल यह फ़ंक्शन जोड़ना होगा:

rdom () { local IFS=\> ; read -d \< E C ;}

अब आप पढ़े हुए जैसे html दस्तावेजों के लिए rdom का उपयोग कर सकते हैं। कब कहा जाता है Rdom तत्व को E और वेरिएंट C को वेरिएबल करेगा।

उदाहरण के लिए, वह करना जो आप करना चाहते थे:

while rdom; do
    if [[ $E = title ]]; then
        echo $C
        exit
    fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt

क्या आपक लिए इसे विस्तार से कहना संभव है? मुझे यकीन है कि यह आपके लिए पूरी तरह से स्पष्ट है .. और यह एक महान जवाब हो सकता है - अगर मैं बता सकता हूं कि आप वहां क्या कर रहे थे .. तो क्या आप इसे थोड़ा और अधिक तोड़ सकते हैं, संभवतः कुछ नमूना आउटपुट उत्पन्न कर सकते हैं?
एलेक्स ग्रे

1
मूल के लिए श्रेय - यह एक-लाइनर बहुत अजीब और अद्भुत है।
मवरिक

1
महान हैक, लेकिन मुझे शेल के विस्तार और अंत लाइनों की सही व्याख्या (एन्कोडिंग पर निर्भर करता है)
e3317174

8
XML को grep और awk के साथ पार्स करना ठीक नहीं है । यह एक स्वीकार्य समझौता हो सकता है यदि एक्सएमएल पर्याप्त सरल हैं और आपके पास बहुत अधिक समय नहीं है, लेकिन इसे कभी भी अच्छा समाधान नहीं कहा जा सकता है।
पेटेर - मोनिका

59

शेल स्क्रिप्ट से कॉल की जा सकने वाली कमांड लाइन टूल में शामिल हैं:

  • 4xpath - पायथन के 4Suite पैकेज के चारों ओर कमांड-लाइन रैपर
  • XMLStarlet
  • xpath - पर्ल की XPath लाइब्रेरी के चारों ओर कमांड-लाइन रैपर
  • Xidel - URL के साथ-साथ फ़ाइलों के साथ भी काम करता है। JSON के साथ भी काम करता है

मैं xmllint और xsltproc का उपयोग कमांड लाइन या शेल स्क्रिप्ट में XML प्रोसेसिंग करने के लिए थोड़ा XSL ट्रांसफॉर्म स्क्रिप्ट के साथ करता हूं।


2
मैं 'xpath' या '4xpath' कहाँ से डाउनलोड कर सकता हूँ?
Opher

3
हां, एक दूसरा वोट / अनुरोध - जहां उन टूल को डाउनलोड करना है, या क्या आप मतलब है कि एक को मैन्युअल रूप से एक आवरण लिखना है? जब तक आवश्यक न हो मैं समय बर्बाद नहीं करूँगा।
डेविड

4
sudo apt-get install libxml-xpath-perl
एंड्रयू वैगनर

22

आप xpath उपयोगिता का उपयोग कर सकते हैं। यह पर्ल एक्सएमएल-एक्सपीथ पैकेज के साथ स्थापित है।

उपयोग:

/usr/bin/xpath [filename] query

या XMLStarlet । खुलने के उपयोग पर इसे स्थापित करने के लिए:

sudo zypper install xmlstarlet

या cnf xmlअन्य प्लेटफार्मों पर प्रयास करें ।


5
अपने खुद के सीरियलाइज़र (जैसा कि अन्य उत्तरों में सुझाया गया है) लिखने से एक्सएमएल स्टारलेट का उपयोग करना निश्चित रूप से एक बेहतर विकल्प है।
ब्रूनो वॉन पेरिस

कई प्रणालियों पर, xpathजो प्रीइंस्टॉल्ड आता है, स्क्रिप्ट में एक घटक के रूप में उपयोग के लिए अनुपयुक्त है। उदाहरण के लिए देखें stackoverflow.com/questions/15461737/… एक विस्तार के लिए।
ट्रिपल जूल

2
उबंटू / डेबियन परapt-get install xmlstarlet
rubo77

12

यह पर्याप्त है ...

xpath xhtmlfile.xhtml '/html/head/title/text()' > titleOfXHTMLPage.txt

धन्यवाद, जल्दी और मेरे लिए काम किया
मिगुएल मोटा

1
डेबियन में apt-get install libxml-xpath-perl
tres.14159

आकर्षण की तरह काम करता है
एलेक्जेंड्रू-मिहाई मनोलेस्कु


5

चाड के जवाब से शुरू, यहाँ UML को पार्स करने के लिए पूर्ण कार्य समाधान है, टिप्पणियों के प्रॉपर हैंडलिंग के साथ, सिर्फ 2 छोटे कार्यों (2 बू से अधिक आप उन सभी को मिला सकते हैं)। मैं नहीं कहता कि चाड का काम बिल्कुल नहीं था, लेकिन इसमें बहुत बुरी तरह से तैयार एक्सएमएल फाइलों के साथ बहुत अधिक समस्याएं थीं: इसलिए आपको टिप्पणियों और गलत स्थानों / सीआर / टीएबी / आदि को संभालने के लिए थोड़ा और मुश्किल होना चाहिए।

इस उत्तर का उद्देश्य तैयार-2-उपयोग को देना है, किसी को भी यूएमएल को पार्सल, पाइथन या किसी अन्य चीज का उपयोग किए बिना जटिल उपकरणों को पार्स करने की जरूरत है। मेरे लिए, मैं पुराने उत्पादन OS के लिए cpan, और perl मॉड्यूल को स्थापित नहीं कर सकता, जिस पर मैं काम कर रहा हूं, और अजगर उपलब्ध नहीं है।

सबसे पहले, इस पोस्ट में उपयोग किए गए यूएमएल शब्दों की एक परिभाषा:

<!-- comment... -->
<tag attribute="value">content...</tag>

EDIT: अपडेट किए गए कार्य, जिनके हैंडल के साथ:

  • Websphere xml (xmi और xmlns विशेषताएँ)
  • 256 रंगों के साथ एक संगत टर्मिनल होना चाहिए
  • २४ रंगों की ग्रे
  • अनुकूलता IBM AIX बैश 3.2.16 (1) के लिए जोड़ी गई

कार्य, पहले xml_read_dom है जिसे xml_read द्वारा पुनरावर्ती कहा जाता है:

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
}

और दूसरा वाला:

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
}

और अंत में, rtrim, ट्रिम और इको 2 (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; }

colorization:

ओह, और आपको पहले से परिभाषित किए जाने वाले कुछ साफ-सुथरे गतिशील चर की आवश्यकता होगी, और निर्यात भी:

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"'

उस सभी सामान को कैसे लोड करें:

या तो आप जानते हैं कि कैसे फ़ंक्शंस बनाने और उन्हें FPATH (ksh) या FPATH (बैश) के अनुकरण के माध्यम से लोड करना है

यदि नहीं, तो बस कमांड लाइन पर सब कुछ कॉपी / पेस्ट करें।

यह कैसे काम करता है:

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

डीबग मोड (-d) टिप्पणियों के साथ और पार्स की गई विशेषताओं को stderr पर प्रिंट किया जाता है


मैं उपरोक्त दो कार्यों का उपयोग करने की कोशिश कर रहा हूं जो निम्नलिखित उत्पन्न करता है ./read_xml.sh: line 22: (-1): substring expression < 0:?
खमरबाइसे

पंक्ति 22:[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
ख़मरबाइज़

सॉरी खमारबाइज़, ये बैश शेल फंक्शन हैं। यदि आप उन्हें शेल स्क्रिप्ट के रूप में अनुकूलित करना चाहते हैं, तो आपको निश्चित रूप से कुछ मामूली अनुकूलन की उम्मीद करनी होगी! इसके अलावा अद्यतन किए गए कार्य आपकी त्रुटियों को संभालते हैं;)
मेहतर

4

मुझे किसी शुद्ध शेल XML पार्सिंग टूल के बारे में जानकारी नहीं है। इसलिए आपको सबसे अधिक दूसरी भाषा में लिखे गए टूल की आवश्यकता होगी।

मेरा XML :: Twig Perl मॉड्यूल एक ऐसा उपकरण है xml_grep, जहां आप शायद वही लिखेंगे जो आप चाहते हैं xml_grep -t '/html/head/title' xhtmlfile.xhtml > titleOfXHTMLPage.txt( -tविकल्प आपको xml के बजाय पाठ के रूप में परिणाम देता है)


4

एक और कमांड लाइन टूल मेरी नई Xidel है । यह पहले से उल्लेख किए गए xpath / xmlstarlet के विपरीत, XPath 2 और XQuery का भी समर्थन करता है।

शीर्षक इस तरह पढ़ा जा सकता है:

xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt

और इसमें कई वैरिएबल को बैश करने के लिए निर्यात करने के लिए एक अच्छी सुविधा है। उदाहरण के लिए

eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )

$titleशीर्षक और $imgcountफ़ाइल में छवियों की संख्या के लिए सेट , जो इसे सीधे बैश में पार्स करने के समान लचीला होना चाहिए।


मुझे ठीक इसी की आवश्यकता थी! :)
थॉमस दौगार्ड

2

ठीक है, आप xpath उपयोगिता का उपयोग कर सकते हैं। मुझे लगता है कि पर्ल के XML :: Xpath में यह शामिल है।


2

एक्सएमएल फाइलों में फाइल पथों के लिनक्स और विंडोज प्रारूपों के बीच अनुवाद के लिए कुछ शोध के बाद मुझे दिलचस्प ट्यूटोरियल और समाधान मिले:


2

हालांकि, कुछ तैयार-तैयार कंसोल उपयोगिताओं हैं जो आप जो चाहते हैं वह कर सकते हैं, संभवतः एक सामान्य प्रयोजन प्रोग्रामिंग भाषा में कोड की कुछ पंक्तियों को लिखने में कम समय लगेगा, जैसे कि पायथन जो आप आसानी से बढ़ा सकते हैं और अनुकूलित कर सकते हैं आपकी ज़रूरतें।

यहाँ एक पाइथन लिपि है जो lxmlपार्सिंग के लिए उपयोग होती है - यह पहले पैरामीटर के रूप में एक फ़ाइल या URL का नाम लेती है, दूसरे पैरामीटर के रूप में एक XPath एक्सप्रेशन, और दिए गए एक्सप्रेशन से मेल खाते स्ट्रिंग्स / नोड्स को प्रिंट करता है।

उदाहरण 1

#!/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के साथ स्थापित किया जा सकता है pip install lxml। Ubuntu पर आप उपयोग कर सकते हैं sudo apt install python-lxml

प्रयोग

python xpath.py myfile.xml "//mynode"

lxml एक URL को इनपुट के रूप में भी स्वीकार करता है:

python xpath.py http://www.feedforall.com/sample.xml "//link"

नोट : यदि आपके XML में कोई उपसर्ग (जैसे xmlns=http://abc...) के साथ एक डिफ़ॉल्ट नाम स्थान है तो आपको pअपने भावों में उपसर्ग ('हैक' द्वारा प्रदान) का उपयोग करना होगा, जैसे //p:moduleकि किसी pom.xmlफ़ाइल से मॉड्यूल प्राप्त करना । यदि pआपके एक्सएमएल में पहले से ही उपसर्ग मैप किया गया है, तो आपको दूसरे उपसर्ग का उपयोग करने के लिए स्क्रिप्ट को संशोधित करना होगा।


उदाहरण 2

एक बंद स्क्रिप्ट जो एक अपाचे मावेन फ़ाइल से मॉड्यूल नामों को निकालने के संकीर्ण उद्देश्य को पूरा करती है। ध्यान दें कि नोड नाम ( module) डिफ़ॉल्ट नामस्थान के साथ कैसे उपसर्ग किया गया है {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>

mod_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ओवर apt-getया yumकॉल को सही ठहरा सकता हूं । धन्यवाद!
ई। मोफत

0

फ़ंक्शन और चर असाइनमेंट में <और >संकेतों के क्रम को उलटाकर यूज़ेम की विधि में सुधार किया जा सकता है rdom, ताकि:

rdom () { local IFS=\> ; read -d \< E C ;}

हो जाता है:

rdom () { local IFS=\< ; read -d \> C E ;}

यदि पार्सिंग इस तरह से नहीं की जाती है, तो XML फ़ाइल में अंतिम टैग कभी नहीं पहुंचता है। यह समस्याग्रस्त हो सकता है यदि आप whileलूप के अंत में एक और XML फ़ाइल आउटपुट करने का इरादा रखते हैं ।


0

यदि आप 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

-1

जबकि ऐसा लगता है कि "XML पार्स कभी नहीं, JSON ... एक उचित उपकरण के बिना बैश से" ध्वनि सलाह है, मैं असहमत हूं। यदि यह साइड जॉब है, तो उचित टूल की तलाश में कमरबंद है, तो इसे सीखें ... अज़क मिनटों में कर सकते हैं। मेरे कार्यक्रमों को उपरोक्त सभी और अधिक प्रकार के डेटा पर काम करना होगा। नरक, मैं 5-7 मिनट के लिए अलग-अलग स्वरूपों को पार्स करने के लिए 30 टूल का परीक्षण नहीं करना चाहता हूं, अगर मुझे समस्या को मिनटों में जगाया जा सकता है। मुझे XML, JSON या जो भी हो उसकी परवाह नहीं है! मुझे उन सभी के लिए एक ही समाधान चाहिए।

एक उदाहरण के रूप में: मेरा स्मार्टहोम प्रोग्राम हमारे घरों को चलाता है। इसे करते समय, यह बहुत सारे अलग-अलग प्रारूपों में डेटा के ढेरों को पढ़ता है जिन्हें मैं नियंत्रित नहीं कर सकता। मैं कभी भी समर्पित, उचित साधनों का उपयोग नहीं करता हूं क्योंकि मैं उन आंकड़ों को पढ़ने में मिनटों से अधिक खर्च नहीं करना चाहता जो मुझे चाहिए। एफएस और आरएस समायोजन के साथ, यह जाग समाधान किसी भी पाठ्य प्रारूप के लिए पूरी तरह से काम करता है। लेकिन, यह उचित उत्तर नहीं हो सकता है जब आपका प्राथमिक कार्य मुख्य रूप से उस प्रारूप में डेटा के भार के साथ काम करना है!

कल टकराव से XML को पार्स करने की समस्या का सामना करना पड़ा। यहाँ मैं इसे किसी भी पदानुक्रमित डेटा प्रारूप के लिए कैसे करता हूँ। एक बोनस के रूप में - मैं डेटा को सीधे बैश स्क्रिप्ट में वेरिएबल्स में असाइन करता हूं।

पढ़ने के लिए आसान बनाने के लिए, मैं चरणों में समाधान प्रस्तुत करूंगा। ओपी परीक्षण डेटा से, मैंने एक फ़ाइल बनाई: test.xml

पार्सिंग ने एक्सएमएल को बाश में कहा और डेटा को 90 वर्णों में निकाला:

awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml

मैं आमतौर पर अधिक पठनीय संस्करण का उपयोग करता हूं क्योंकि वास्तविक जीवन में इसे संशोधित करना आसान है क्योंकि मुझे अक्सर अलग-अलग परीक्षण करने की आवश्यकता होती है:

awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml

मुझे परवाह नहीं है कि प्रारूप को कैसे कहा जाता है। मैं केवल सबसे सरल उपाय चाहता हूं। इस विशेष मामले में, मैं डेटा से देख सकता हूं कि न्यूलाइन रिकॉर्ड सेपरेटर (आरएस) और <> सीमांकित फ़ील्ड (एफएस) है। मेरे मूल मामले में, मेरे पास दो रिकॉर्ड के भीतर 6 मानों की जटिल अनुक्रमणिका थी, उनसे संबंधित, यह ढूंढें कि डेटा कब मौजूद है (फ़ील्ड) रिकॉर्ड्स मौजूद हैं या नहीं। समस्या को पूरी तरह से हल करने के लिए जाग की 4 लाइनें लगीं। इसलिए, उपयोग करने से पहले प्रत्येक आवश्यकता के लिए विचार को अनुकूलित करें!

दूसरा भाग बस यह देखता है कि एक पंक्ति (RS) में वांछित स्ट्रिंग है और यदि ऐसा है, तो आवश्यक फ़ील्ड (FS) प्रिंट करता है। उपरोक्त ने मुझे लगभग 30 सेकंड तक कॉपी किया और अंतिम कमांड से मैंने इस तरह से उपयोग किया (4 बार लंबा)। और वह यह है! 90 वर्णों में किया गया।

लेकिन, मुझे हमेशा अपनी स्क्रिप्ट में डेटा को चर में प्राप्त करने की आवश्यकता होती है। मैं पहली बार इस तरह से निर्माणों का परीक्षण करता हूं:

awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml

कुछ मामलों में मैं प्रिंट के बजाय प्रिंटफ का उपयोग करता हूं। जब मैं देखता हूं कि सब कुछ ठीक है, तो मैं बस चर मानों को असाइन करना समाप्त करता हूं। मुझे पता है कि बहुत से "बुराई" "बुराई" है, टिप्पणी करने की कोई आवश्यकता नहीं है :) सालों से मेरे चारों नेटवर्क पर ट्रिक पूरी तरह से काम करती है। लेकिन सीखते रहें अगर आपको समझ में नहीं आता है कि यह बुरा अभ्यास क्यों हो सकता है! बैश चर असाइनमेंट और पर्याप्त रिक्ति सहित, मेरे समाधान को सब कुछ करने के लिए 120 वर्णों की आवश्यकता है।

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"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.