Can BASH handle mathematical operations and do a Search & Replace?

Hello,
I have a bunch of xml file that needs to have edits made and I was wondering if a BASH script could handle it.

I would like the script to look within my xml files and replace all integers greater than 5px with a value that is 25% smaller. For example, 100px = 75px. Since the integers I'm looking to change has the following format: xCssStyle="margin-right:3px;">, I'm guessing I should do a grep xCssStyle="margin-right:3px;"> file.xml

I'm just not sure if BASH can look for a string containing "px" as a suffix, do a mathematical calculation, and then do a replace.

100px=25px is 25% smaller? looks like it's 75% smaller.
Please post a sample input file and a desired output.
Do the posting using the code tags.

vgersh99, you are correct. Decreasing by 25% will give me 75% of the original.

Here is a sample xml input:

<xControl xId="" xType="Label" xRow="1" xColumn="1" xCssStyle="margin-right:100px;">
                    <xValue xType="constant">LEVEL1</xValue>
</xControl>
<xControl xId="" xType="Label" xRow="1" xColumn="1" xCssStyle="margin-right:200px;">
                    <xValue xType="constant">LEVEL2</xValue>
</xControl>
<xControl xId="" xType="Label" xRow="1" xColumn="1" xCssStyle="margin-right:4px;">
                    <xValue xType="constant">LEVEL3</xValue>
</xControl>

and the output should be a file with the following changes:

<xControl xId="" xType="Label" xRow="1" xColumn="1" xCssStyle="margin-right:75px;">
                    <xValue xType="constant">LEVEL1</xValue>
</xControl>
<xControl xId="" xType="Label" xRow="1" xColumn="1" xCssStyle="margin-right:150px;">
                     <xValue xType="constant">LEVEL2</xValue>
 </xControl>
<xControl xId="" xType="Label" xRow="1" xColumn="1" xCssStyle="margin-right:4px;">
                     <xValue xType="constant">LEVEL3</xValue>
 </xControl>

I know the starting point would be to have a FOR loop that will look in the current directory, but I'm not sure how I should proceed from there.

---------- Post updated at 08:28 PM ---------- Previous update was at 08:26 PM ----------

OK, I've been running some tests to see if I can do a mathematical expression followed by a "Search and Replace," but didn't have much luck. Since the pixel sizes I want to change is between 5-200, would it be wise to create a bunch of case statements?

something along these line:
nawk -f jl.awk myFile.xml

jl.awk:

match($0,"margin-right:[^;][^;]*") {
    str=substr($0,RSTART,RLENGTH)
    gsub("[^0-9]","",str)
    str-=(str/4)
    $0=substr($0,1,RSTART-1) "margin-right:" str "px;" substr($0,RSTART+RLENGTH+1)
}
1

vgersh99, thanks for your help (both in this thread and throughout the forums). After searching the forums, I found something that may work (code that you originally worked on!), but I'm having some trouble modifying it.

What I did was create a file called convert, that has the conversions already made:
6px 5px
7px 5px
8px 6px
9px 7px
10px 8px
11px 8px
12px 9px
.....

I also created another file called file (for testing purposes) to test the replacements. I figured if a keyword is found in column one, replace it with the conversion in column two.

#!/bin/bash

while read line
do
KEY=$(echo $line | awk '{ print $1 }')
VALUE=$(echo $line | awk '{ print $2 }')
sed -e "s/${KEY}/${VALUE}/g" file > file.tmp
mv file.tmp file
done < convert

Since (I believe) awk is used for files in column format, I don't think that this code will work. Is there a way around this?

have you tried the code I've posted?

1 Like

I tried running your code and realized that it prints out the output, rather than saving the file. How come when I do a

command, it doesn't create an output file?

---------- Post updated at 10:58 PM ---------- Previous update was at 10:29 PM ----------

nevermind.... made a dumb mistake. vgersh99 thanks for the help!

---------- Post updated at 11:46 PM ---------- Previous update was at 10:58 PM ----------

sorry for bringing back what I thought was a "dead thread"

I realized that some of the xml files has the following lines:

<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:20px;margin-right:3px;">

Therefore, both margin-left and margin-right would need to be changed. What I did was create the following bash file:

nawk -f jl1.awk myFile.xml > myFile.tmp
mv myFile.tmp myFile.xml

nawk -f jl2.awk myFile.xml > myFile.tmp
mv myFile.tmp myFile.xml

where jl1.awk is the following:

match($0,"margin-right:[^;][^;]*") {
    str=substr($0,RSTART,RLENGTH)
    gsub("[^0-9]","",str)
    str-=(str/4)
    $0=substr($0,1,RSTART-1) "margin-right:" str "px;\">"
substr($0,RSTART+RLENGTH+1)
}
1

and jl2.awk is:

match($0,"margin-left:[^;][^;]*") {
    str=substr($0,RSTART,RLENGTH)
    gsub("[^0-9]","",str)
    str-=(str/4)
    $0=substr($0,1,RSTART-1) "margin-left:" str "px;"
substr($0,RSTART+RLENGTH+1)
}
1

jl1.awk seems to work fine, but when I run jl2.awk, the margin-right information is deleted. Why is this?

BEGIN {
  dirN=split("right left", dirA, FS)
}
/margin-/ {
    for(i=1;i<=dirN;i++)
      if (match($0,"margin-" dirA ":[^;][^;]*")) {
        str=substr($0,RSTART,RLENGTH)
        gsub("[^0-9]","",str)
        str-=(str/4)
        $0=substr($0,1,RSTART-1) "margin-" dirA ":" str "px;" substr($0,RSTART+RLENGTH+1)
     }
}
1

vgersh99, I've tested your code and found that it converts all "margin-right" px. And if a "margin-left" is present, it converts the "margin-left," but removes "margin-right."

Here is a sample file:

<xControl xId="" xType="Label" xRow="1" xColumn="1"  xCssStyle="margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="unitLoss" xType="ComboView" xRow="1" xColumn="1" xCssStyle="margin-right:20px;">

After running the updated code, I get the following:

<xControl xId="" xType="Label" xRow="1" xColumn="1"  xCssStyle="margin-right:2.25px;
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:15px;
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:15px;
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:15px;
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:15px;
<xControl xId="unitLoss" xType="ComboView" xRow="1" xColumn="1" xCssStyle="margin-right:15px;

Given your latest sample input, I get:

<xControl xId="" xType="Label" xRow="1" xColumn="1"  xCssStyle="margin-right:2.25px;">
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:15px;margin-right:2.25px;">
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:15px;margin-right:2.25px;">
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:15px;margin-right:2.25px;">
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:15px;margin-right:2.25px;">
<xControl xId="unitLoss" xType="ComboView" xRow="1" xColumn="1" xCssStyle="margin-right:15px;">

maybe I am missing something?
nawk -f jl2.awk myFile.xml

jl2.awk

#!/bin/bash

BEGIN {
  dirN=split("right left", dirA, FS)
}
/margin-/ {
    for(i=1;i<=dirN;i++)
      if (match($0,"margin-" dirA ":[^;][^;]*")) {
        str=substr($0,RSTART,RLENGTH)
        gsub("[^0-9]","",str)
        str-=(str/4)
        $0=substr($0,1,RSTART-1) "margin-" dirA ":" str "px;"
substr($0,RSTART+RLENGTH+1)
     }
}
1

myFile.xml:

<xControl xId="" xType="Label" xRow="1" xColumn="1"  xCssStyle="margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:20px;margin-right:3px;">
<xControl xId="unitLoss" xType="ComboView" xRow="1" xColumn="1" xCssStyle="margin-right:20px;">

output:

<xControl xId="" xType="Label" xRow="1" xColumn="1"  xCssStyle="margin-right:2.25px;
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:15px;
<xControl xId="" xType="Label" xRow="1" xColumn="3"  xCssStyle="margin-left:15px;
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:15px;
<xControl xId="" xType="Label" xRow="1" xColumn="5"  xCssStyle="margin-left:15px;
<xControl xId="unitLoss" xType="ComboView" xRow="1" xColumn="1" xCssStyle="margin-right:15px;

you have a wrapped line AND you don't need '#!/bin/bash':

BEGIN {
  dirN=split("right left", dirA, FS)
}
/margin-/ {
    for(i=1;i<=dirN;i++)
      if (match($0,"margin-" dirA ":[^;][^;]*")) {
        str=substr($0,RSTART,RLENGTH)
        gsub("[^0-9]","",str)
        str-=(str/4)
        $0=substr($0,1,RSTART-1) "margin-" dirA ":" str "px;" substr($0,RSTART+RLENGTH+1)
     }
}
1
1 Like

i'm a dummy. you're my hero!