1

I have some XML that looks something like this:

    <artifactId>myproject</artifactId>
    <version>1.14.0-SNAPSHOT</version>

I want to extract the version, i.e. 1.14.0-SNAPSHOT. I know how to do it using two greps and a pipe:

$ grep -A1 "<artifactId>myproject</artifactId>" pom.xml | grep -Eo "\d+.\d+.\d+-SNAPSHOT"

How can I combine the two into one? Would I be better off using awk or sed for this task?

  • 6
    Why don't you use a tool designed to parse/process xml ? – don_crissti Aug 15 '16 at 20:10
  • @don_crissti fair question, but I want to be able to share this script with the rest of my team without asking them to install any special packages. – David Kennedy Aug 15 '16 at 20:12
2

If you sure for <version> on next line after myproject

sed -n '
    \|<artifactId>myproject</artifactId>|{
        n                                           #get next line
        s|[[:blank:]]*</\?version>[[:blank:]]*||gp  #remove tags and print
    }
' pom.xml

Or if you have pgrep

grep -zoP '<artifactId>myproject</artifactId>\s*\n\s*<version>\K[^<]+' pom.xml
  • That sed command throws an error: sed: 3: " \|<artifactId>mypr ...": extra characters at the end of n command – David Kennedy Aug 25 '16 at 16:14
2

You've mentioned in comments that you want something portable. That's admirable, but I'd really urge you to not do that anyway. XML is a contextual language, and regex isn't. There's simply no way a regular expression can parse XML correctly as a result.

At best - you get a hack, that's only valid provided the XML stays in the same format. But as the XML spec says that format can chance and retain the same semantics, that's a dangerous assumption and creates brittle code.

I know you have reasons to want to do this - you already have an answer that gives you a way to. I would suggest that a parser is still the right answer.

But with XML parsers, you get xpath - which is a lot like regular expressions, but is applicable to hierarchical information.

Something like this:

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

use XML::Twig;

my $twig = XML::Twig -> parse ( \*DATA );

my $version = $twig -> get_xpath('//item/artifactId[string()="myproject"]/../version',0)->text;
print $version;

__DATA__
<xml>
  <item>
    <artifactId>myproject</artifactId>
    <version>1.14.0-SNAPSHOT</version>
  </item>
</xml>

Hopefully you can see how that xpath is working? //item finds an item anywhere within the structure. [string()= queries the text content. You could instead do something like [@someAtt="fish"] to check an attribute.

Then we .. up (to item) and get the version element. Then get the text value.

As a oneliner:

perl -MXML::Twig -0777 -e 'print XML::Twig -> parse ( <> ) -> get_xpath('/item/artifactId[string()="myproject"]/../version',0)->text,"\n"    yourxmlfile.xml

Now, I'm suggesting XML::Twig because I think it's easier to learn. XML::LibXML is also quite good.

But it's distributed with strawberry perl on Windows, and is readily available in a lot of package managers - or from CPAN.

Alternatively - xmlstarlet should allow you to do much the same thing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.