52

I want to get the value of name and put it in a variable using XMLLint

<body>
<value name="abc"></value>
</body>

echo 'cat //body/value/@name' | xmllint --shell "test.xml"

/ >  -------
 name="abc"
/ > 

So I want to assign the value "abc" to variable $test

0

6 Answers 6

57

You need to use fn:string(), which will return the value of its argument as xs:string. In case its argument is an attribute, it will therefore return the attribute's value as xs:string.

test=$(xmllint --xpath "string(//body/value/@name)" test.xml)
Sign up to request clarification or add additional context in comments.

3 Comments

unfortunately --xpath is unsupported on a lot of installations
This only shows the value for a single element, is it possible to get multiple matches?
@CiroSantilli郝海东冠状病六四事件法轮功 You can get multiple matches with xmllint --xpath "//body/value/@name", but they will be all on the same line, unless you have version 2.9.9 or newer of the package libmxl2-utils.
8

Try this, it's not beautiful but it works :)

I just erase lines containing > from stdout , cut the string to get the second part after the = , and delete "

test=$(echo 'cat //body/value/@name' | xmllint --shell "test.xml" | grep -v ">" | cut -f 2 -d "=" | tr -d \"); 
echo $test

2 Comments

Use xmllint so you don't have to use REs to parse XML. Realize you have to use REs to parse the output of xmllint.
why does it print ------- before the name attribute value? how to remove it?
6

An approach with a helper awk command that supports multiple attributes (a streamlined version of ego's approach):

echo 'cat //*/@name' | xmllint --shell file | awk -F\" 'NR % 2 == 0 { print $2 }'

The awk command:

  • splits xmllint's output lines into fields by " chars. (-F\")

    • Note that xmllint normalizes quoting around attribute values to "..." on output, even if the input had '...', so it's sufficient to split by ".
  • only processes even-numbered lines (NR %2 == 0), thereby filtering out the separator lines that cat invariably prints.

  • print $2 then prints only the 2nd field, which is the value of each attribute without the enclosing "...".

Assuming the following sample XML in file:

<body>
  <value name="abc"></value>
  <value name="def"></value>
</body>

the above yields:

abc
def

2 Comments

this works perfectly for me, thanks. Do you also have a nice way of assigning the values to different variables. Like VAR_1=$(echo 'cat //*/@name' | xmllint --shell file | awk -F\" 'NR % 2 == 0 { print $2 }') ?
@dieHellste: You can read the output lines into variables (either in a while loop or, in Bash, into an array with read -a); if you need further guidance, please ask a new question.
3

I recently had to port my original simpler solution using --xpath to a platform lacking this feature, so had to adopt the "cat" solution too. This will handle multiple matches, tested on Ubuntu 12.04 and Solaris 11:

getxml() { # $1 = xml file, $2 = xpath expression
    echo "cat $2" | xmllint --shell $1 |\
    sed -n 's/[^\"]*\"\([^\"]*\)\"[^\"]*/\1/gp'
}

e.g. extracting instance names from a glassfish domain config:

$ getxml /tmp/test.xml "//server[@node-ref]/@name"
inst1
inst2

The sed post-processing just grabs all quoted values which was adequate for my needs (getting bits of glassfish config).

Comments

2

With the goal of matching multiple lines and extract the value of their homonymous attribute and building on previous answers this worked for me and it's shorter hehe

xmllint --xpath "//*/@value" file.xml | awk -F\" '{ print $2 }'

1 Comment

even shorter :) xmllint --xpath "//*/@value" file.xml | cut -f2 -d\"
-1

The next works for me:

xmllint --xpath '//o/@loc' file.xml | sed -n 's/[^\"]*\"\([^\"]*\)\"[^\"]*/\1\n/gp'

1 Comment

//o/@loc??? And why would one need this awful sed-command?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.