Standalone perl script:
#!/usr/bin/perl
use strict;
use XML::LibXML;
use List::Util qw(max);
my $filename = './input.xml';
my $dom = XML::LibXML->load_xml(location => $filename);
my @ids = map { $_->to_literal() } $dom->findnodes('/data/element/id');
print max(@ids), "\n";
Uglier, harder-to-understand and harder-to-edit one-liner version:
perl -MXML::LibXML -MList::Util=max -e '
$dom = XML::LibXML->load_xml(location => shift);
@ids = map { $_->to_literal() } $dom->findnodes("/data/element/id");
print max(@ids), "\n";' input.xml
Note: both of the above assume that the <element>
s are wrapped inside a <data>
path. If not, adjust the xpath in the findnodes()
function call to suit your actual data.
I ran them both with the following input.xml
file:
<?xml version='1.0' encoding='UTF-8'?>
<data>
<element>
<id>0</id>
<tag1>something</tag1>
<tagn>something</tagn>
</element>
<element>
<id>1</id>
<tag1>something</tag1>
<tagn>something</tagn>
</element>
<element>
<id>2</id>
<tag1>something</tag1>
<tagn>something</tagn>
</element>
<element>
<id>3</id>
<tag1>something</tag1>
<tagn>something</tagn>
</element>
<element>
<id>4</id>
<tag1>something</tag1>
<tagn>something</tagn>
</element>
<element>
<id>5</id>
<tag1>something</tag1>
<tagn>something</tagn>
</element>
</data>
and both got the correct result of 5.
BTW, either version can be made to read STDIN by changing the lines with location => $filename
or location => shift
to:
my $dom = XML::LibXML->load_xml(IO => *STDIN);
The my
is optional in the one-liner version without use strict
, but is required in the stand-alone version.
BTW, it's also easy to modify either script so that both the input filename and the xpath can be specified on the command line. so that you have a generic tool for getting the max()
value of any xpath element. e.g.
#!/usr/bin/perl
use strict;
use XML::LibXML;
use List::Util qw(max);
my $dom = XML::LibXML->load_xml(location => shift);
my @ids = map { $_->to_literal() } $dom->findnodes(shift);
print max(@ids), "\n";
Run as, e.g.,
$ xml-max.pl input.xml /data/element/id
5
END
codeblocks:perl -nle '$m = $1 if m{<id>(\d+)</id>} and $1 > $m; END{print $m}'
– mosvy Sep 25 at 22:37XML::Parser::Expat
(orXML::LibXML::SAX
andReader
). Do not use the default DOM interface ofXML::LibXML
or any other DOM tool likexmlstarlet
other than for useless toys and demos (and for answering Qs on stackexchange ;-)) as those will first load the whole file into memory, and are able to bring a machine to its knees even with moderately sized xmls (smaller than 1G). – mosvy Sep 25 at 23:13xmlstarlet
works fine too (but the command-line options are a PITA to remember so I prefer perl). For extremely large files, it would make sense to iterate through the elements without reading them all into RAM at once. Your panic is overblown. The sky isn't falling. – cas Sep 26 at 2:28