Issue
I would like to extract a float from a coverage report and multiply it with 100 to report it as percentage.
I am able to match the float using sed
or grep
and then multiply it using awk
, but I was wondering if there is a more elegant single tool solution, maybe with perl
or awk
only?
The line looks like this:
<coverage line-rate="0.34869999999999995" branch-rate="0.2777" >
My current solution:
sed -n 's/^<coverage line-rate="\([0-9\.]*\)".*$/\1/p' a.txt | awk '{print (100 * $1)}'
34.87
Solution
A way to capture a number and process it, if matched, with Perl
perl -wnE'say 100 * $1 if /^<coverage line-rate="([0-9]*\.[0-9]*)"/' file
Another way to extract captures and process (multiply and print) them
perl -wnE'say 100 * $_ for /^<coverage line-rate="([0-9]*\.[0-9]*)"/' file
This uses the property of the match operator (/.../
) to return the matches when in list context, provided by the for
loop, in which we then process matches (only one in this case), multiply and print. When there is no match the loop just won't have items to iterate over.
Both these process a file and merely print the result from lines that match, like in the question. In both cases if a particular precision is wanted the say
can be replaced by printf ".2f\n", $x;
(two decimal places, etc).
I am not sure whether printing a number out of a one-liner is indeed the ultimate need or merely a simplified presentation for this purpose, but there are of course yet other ways.
One can also index into the list with matches in order to extract a captured pattern
my $num = ( /^<coverage line-rate="([0-9]*\.[0-9]*)/ )[0];
and then process after a check (whether there was a match), $num *= 100 if defined $num;
In case the input string need be changed, as is done in the question, perhaps for further work, to a particular precision
# Keep four decimal places
s/^<coverage line-rate="([0-9]*\.[0-9]*).*/sprintf("%.4f", 100*$1)/e;
or if the precision doesn't matter and can be left to the interpreter
s/^<coverage line-rate="([0-9]*\.[0-9]*).*/100*$1/e;
In both cases the /e
modifier makes the replacement part be evaluated as code, so we can do some processing there. In the first case I use sprintf
to format the replacement string while in the other case it's just multiplied.
There could be a potential complication here, about how many digits need be kept. If it should be the same number as in the original then we may need to first detect how many that was
use warnings;
use strict;
use feature 'say';
# Number is shortened for demonstration
my $str = shift // q(<coverage line-rate="0.348691" branch-rate="0.2777" >);
sub process_num {
my ($num, $frac) = @_;
$num *= 100;
my $frac_len = length($frac) - 2; # keeps same digits
return sprintf "%.${frac_len}f", $num;
}
$str =~ s/^<coverage line-rate="([0-9]*\.([0-9]+)).*/process_num($1, $2)/e;
say $str;
Running this without arguments prints 34.8691
instead of the original line with 0.348691
Thus assumes the number to have digit(s) for the fractional part, at least.
Answered By - zdim Answer Checked By - Mildred Charles (WPSolving Admin)