Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find and execute mathematical expression from an xml attribute and replace the value

Tags:

shell

math

xml

sed

awk

Below is an excerpt from some xml files that need processing:

<BirimAdi>Adet</BirimAdi>
<BirimCarpan>1</BirimCarpan>
<HavaleFiyati>0</HavaleFiyati>
<HavaleFiyatiParaBirimi>TL</HavaleFiyatiParaBirimi>
<Price1>0</Price1>
<SatisFiyati1ParaBirimi>TL</SatisFiyati1ParaBirimi>
<Isk1>0</Isk1>
<SatisFiyati2>0</SatisFiyati2>

What I need to do is take the value between tags and do the following mathematical operation on it.

Price1 = round(Price1)-0.1;

The script should do it to all xml files in a specified path.

I have considered using 'sed' or 'awk' but I am not sure this can be done easily in sed. Using xmllint looked too much of an overkill to me. Any ideas? I am new to these utilities so couldn't figure much expect that the regex that will find the bit I am looking for is:

/<\s*Price1[^>]*>([^<]*)<\s*\/\s*Price1\s*>/
like image 362
Code Bunny Avatar asked Feb 17 '23 12:02

Code Bunny


2 Answers

I would use a XML parser for the job. For example, XML::Twig. Here an example:

#!/usr/bin/env perl

use warnings;
use strict;
use XML::Twig;

for my $f ( @ARGV ) {
        my $twig = XML::Twig->new(
                twig_handlers => {
                        'Price1' => sub { $_->set_text( sprintf( "%.1f", int( $_->text) - 0.1 ) ) },
                },
                pretty_print => 'indented',
        )->parsefile( $f )->print;
}

Assuming file name is script.pl and a test file xmlfile with content:

<root>
<BirimAdi>Adet</BirimAdi>
<BirimCarpan>1</BirimCarpan>
<HavaleFiyati>0</HavaleFiyati>
<HavaleFiyatiParaBirimi>TL</HavaleFiyatiParaBirimi>
<Price1>3.3</Price1>
<SatisFiyati1ParaBirimi>TL</SatisFiyati1ParaBirimi>
<Isk1>0</Isk1>
<SatisFiyati2>0</SatisFiyati2>
</root>

Run it like:

perl script.pl xmlfile

That yields:

<root>
  <BirimAdi>Adet</BirimAdi>
  <BirimCarpan>1</BirimCarpan>
  <HavaleFiyati>0</HavaleFiyati>
  <HavaleFiyatiParaBirimi>TL</HavaleFiyatiParaBirimi>
  <Price1>2.9</Price1>
  <SatisFiyati1ParaBirimi>TL</SatisFiyati1ParaBirimi>
  <Isk1>0</Isk1>
  <SatisFiyati2>0</SatisFiyati2>
</root>
like image 120
Birei Avatar answered Feb 19 '23 01:02

Birei


A quick a dirty solution:

perl -pe 's!<(Price1)>(\d+(?:\.\d*)?)</\1>!"<$1>".(int($2+0.5)-0.1)."</$1>"!e'<<XXX
<HavaleFiyatiParaBirimi>TL</HavaleFiyatiParaBirimi>
<Price1>2.3</Price1>
<SatisFiyati1ParaBirimi>TL</SatisFiyati1ParaBirimi>
<Price1>2.5</Price1>
XXX

Output:

<HavaleFiyatiParaBirimi>TL</HavaleFiyatiParaBirimi>
<Price1>1.9</Price1>
<SatisFiyati1ParaBirimi>TL</SatisFiyati1ParaBirimi>
<Price1>2.9</Price1>

But Birei's solution is better by far...

like image 27
TrueY Avatar answered Feb 19 '23 02:02

TrueY