2

I have this XML:

<results><testsuites><testcase name="XXXX (XXXXX)" time="20.3817"><result>success</result></testcase><total_time value="20.3830" /></testsuites></results>

I want to extract the 20.3817.

Trying some sed but not getting to work.

3
  • 3
    How would you like to do this? You tagged your Q sed, please include what you already tried.
    – Jan
    Feb 16, 2016 at 17:04
  • Can you provide a second sample input and output? Just as one cannot extrapolate a line from a single data point, we cannot tell what you need from a single example.
    – jw013
    Feb 16, 2016 at 17:10
  • 2
    sed "s:/.*/20.3817/g" Jul 28, 2016 at 9:59

6 Answers 6

9

Don't use regular expressions to parse XML. Use an XML aware tool, e.g. xmllint:

xmllint --xpath 'string(/results/testsuites/testcase/@time)' file.xml
3

With XMLStarlet:

$ xmlstarlet sel -t -v '//testcase/@time' -nl data.xml
20.3817
1
  • Damn, that's a great tool! I need to work with xml files from the command line and appending multiple -v <xpath> args to print everything in a single line is just what I wanted Apr 27, 2023 at 5:59
2

Use lxprintf:

lxprintf -e "%s\n" total_time/@value test.xml
1
  • When can I find this lxprintf command? (Either a link, or the relevant package name in a common distro will suffice.) Also, your answer looks like it will print "20.3830", not the OP's requested "20.3817".
    – JigglyNaga
    Jul 28, 2016 at 11:42
1

perl has a nice XML parse in XML::Twig:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

use XML::Twig;

print XML::Twig -> parse ( \*DATA ) -> get_xpath('//total_time',0)->att('value');

__DATA__
<results><testsuites><testcase name="XXXX (XXXXX)" time="20.3817"><result>success</result></testcase><total_time value="20.3830" /></testsuites></results>

Or as a one liner:

perl -0777 -MXML::Twig -ne 'print XML::Twig -> parse (<>) -> get_xpath('//total_time',0)->att('value')'
0

You would be far, far better using an XML-aware tool

If it really is a simple case of extracting the value of the time attribute you can use sed. As you'll see from most of the answers here the better approach really is to use a tool that understands XML, but for really simple cases you might get away with using sed.

xml='<results><testsuites><testcase name="XXXX (XXXXX)" time="20.3817"><result>success</result></testcase><total_time value="20.3830" /></testsuites></results>'
echo "$xml" | sed -nr 's/.* time="([0-9.]+).*/\1/p'    # GNU
28.3817
echo "$xml" | sed -n 's/.* time="\([0-9.]*\).*/\1/p'   # Non-GNU
28.3817

Or grep

echo "$xml" | grep -Po '(?<=time=")([0-9.]+)'          # GNU
20.3817

But I'd still prefer something XML-aware like xmlstarlet, which can recognise time as an attribute of <testcase>.

2
  • Sorry, I'm afraid I wouldn't even suggest using sed, for all the reasons I wouldn't suggest trying to put a screw in the wall with a hammer. Whilst it might technically work, it will always be a bodge and produce messy, if technically functional solutions.
    – Sobrique
    Jul 28, 2016 at 11:00
  • @Sobrique I'm with you. However, it is possible the OP is running on a system that doesn't have (cannot have) a sensible way of parsing the XML. So for them, or for a potential future reader in that position, I'd like to offer the sed and grep options. I think I've added enough caveats to the suggestions (although I suppose I ought to rewrite the sed to avoid the GNU extensions...). Jul 28, 2016 at 11:09
0

With xidel:

$ xidel -e '//testcase/@time' file
20.3830

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .