Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get XMLLINT to put --xpath results as an array

Tags:

bash

xml

xmllint

I would like the output of the XMLLINT to get put into an BASH array. But all I can get is a single string. The results will return many hits, none with any pattern that can help parse the returned string.

  • I have tried --format and redirect ">" to a text file.
  • I have tried xpath all instances // and just one /

mcv.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <instance>
        <absolutePath>/abc/def</absolutePath>
    </instance>
    <instance>
        <absolutePath>/abc/hij</absolutePath>
    </instance>
</root>

mcv.sh

#!/usr/bin/bash

declare -a wwArray=()

wwCount=$(xmllint --xpath 'count(//absolutePath)' "mcv.xml")

printf "wwCount: '%s' \n" ${wwCount}

i=1

while [ $i -le ${wwCount} ];
do
        wwExtracted=$(xmllint --xpath '//absolutePath['${i}']/text    ()' "mcv.xml")
        printf " - absolutePath: '%s' \n" ${wwExtracted}
        printf " - index: '%d' \n" ${i}
        let i=i+1
done 

Running this, the output is:

wwCount: '2'
 - absolutePath: '/abc/def/abc/hij'
 - index: '1'
XPath set is empty
 - absolutePath: ''
 - index: '2'

...whereas I would expect it to instead be:

wwCount: '2'
 - absolutePath: '/abc/def'
 - index: '1'
 - absolutePath: '/abc/hij'
 - index: '2'
like image 895
paulhr Avatar asked Feb 05 '19 21:02

paulhr


1 Answers

The smallest change needed to make your existing code work is to add parens before the [$i], like so:

#!/usr/bin/bash
wwCount=$(xmllint --xpath 'count(//absolutePath)' "mcv.xml")
for ((i=1; i<=wwCount; i++)); do
        wwExtracted=$(xmllint --xpath '(//absolutePath)['"$i"']/text()' "mcv.xml")
        printf " - absolutePath: '%s' \n" "$wwExtracted"
        printf " - index: '%d' \n" "$i"
done 

That said, this is really inefficient (running your XPath over and over). Consider switching away from xmllint to use XMLStarlet instead, which can be instructed to insert newlines between output elements, so you can tell bash to load those items directly into a real shell array:

#!/usr/bin/bash
readarray -t items < <(xmlstarlet sel -t -m '//absolutePath' -v './text()' -n <mcv.xml)
printf ' - absolutePath: %s\n' "${items[@]}"

Once you've got contents into an array (as created by readarray above), you can also iterate by index:

for idx in "${!items[@]}"; do
  printf ' - absolutePath: %s\n' "${items[$idx]}"
  printf ' - index: %s\n' "$idx"
done
like image 136
Charles Duffy Avatar answered Oct 18 '22 02:10

Charles Duffy