javed
javed

Reputation: 447

Arithmetic replacement in a text file

I have a file like this:

id=1+5
id=1+9
id=25100+10
xyz=1+
abc=123456
conf_string=LMN,J,IP,25100+1,0,3,1

I would like to replace instances of x+y to the value of (x+y). That is 1+5 is replaced by 6, 25100+1 is replaced by 25001 and so on.

I was trying this with gawk by matching with a regex like /[:digit:]++[+digit:]+/ Using the following I could replace some of the instances.

gawk 'BEGIN {FS = "[=+,]"} ; /[:digit:]++[+digit:]+/ {print $1 "=" ($2 + $3)} ! /[:digit:]++[+digit:]+/ {print $0}' /tmp/1.txt 
id=6
id=10
id=25110
xyz=1+
abc=123456
conf_string=LMN,J,IP,25100+1,0,3,1

I am unsure of how to match and replace (25100+1) in the above example. Ideally, I would like to extract all instances of <number> + <number> and replace it with the sum. It will always be a sum of two numbers.

Upvotes: 4

Views: 993

Answers (3)

agc
agc

Reputation: 8406

Using GNU sed, to wrap some shell script $(( )) around the sums, and then the dangerous eval command to run it:

sed 's#\(.*\)\([0-9]\++[0-9]\+\)\(.*\)#printf "\1$((\2))\3"#e' file

...or, with fewer backslashes \:

sed -r 's#(.*)([0-9]+\+[0-9]+)(.*)#printf "\1$((\2))\3"#e' file

Output of either:

id=6
id=10
id=251010
xyz=1+
abc=123456
conf_string=LMN,J,IP,25101,0,3,1

Upvotes: 2

Sundeep
Sundeep

Reputation: 23677

With GNU awk:

$ awk 'BEGIN{r = @/([0-9]+)\+([0-9]+)/}
       { while(match($0, r, m)) sub(r, m[1] + m[2]) } 1' ip.txt
id=6
id=10
id=25110
xyz=1+
abc=123456
conf_string=LMN,J,IP,25101,0,3,1
  • r=@/([0-9]+)\+([0-9]+)/ save the regex in a variable, [0-9] will match all digits
  • match($0, r, m) will be true if the regex matches, portion matched will be available in m array
  • m[1] + m[2] sum the two numbers
  • For older versions, use awk '{while(match($0, /([0-9]+)\+([0-9]+)/, m)) sub(/([0-9]+)\+([0-9]+)/, m[1] + m[2]) } 1' ip.txt as saving regex in a variable is not supported

Note

  • [:digit:] should be used inside a character class [[:digit:]]
  • ++ should be +\+ as you intend the second one to match + literally

See also: How to coerce AWK to evaluate string as math expression?


With perl you can simply use e flag to evaluate the replacement as code

perl -pe 's/(\d+)\+(\d+)/$1+$2/ge' ip.txt
# or    
perl -pe 's/\d+\+\d+/$&/gee' ip.txt

Upvotes: 6

RavinderSingh13
RavinderSingh13

Reputation: 133640

Could you please try following, written and tested with GNU awk, with shown samples. This solution will take care of conditions in case any 2nd field is starting with + or ending with + , like 4th line in output. Edited code tested on following link https://ideone.com/x2EQ0P

awk '
BEGIN{
  FS=OFS="="
}
{
  num=split($2,array1,",")
  for(i=1;i<=num;i++){
    num1=split(array1[i],array2,"+")
      for(k=1;k<=num1;k++){
        if(num1==1){array1[i]=array2[k] }
        if(array2[k]~/^[0-9]+$/){
           val+=array2[k]
           array1[i]=(array2[1]!=""?"":"+") val (array2[num1]!=""?"":"+")
        }
     }
     val=""
  }
  for(o=1;o<=num;o++){
    value=(value?value ",":"")array1[o]
  }
  $2=value
  value=""
}
1
' Input_file


Explanation: Adding detailed explanation for above.

awk '                                             ##Starting awk program from here.
BEGIN{                                            ##Starting BEGIN section of this code from here.
  FS=OFS="="                                      ##Setting field separator nd output field separator as = here.
}
{
  num=split($2,array1,",")                        ##Splitting 2nd field here.
  for(i=1;i<=num;i++){                            ##Running for loop till value of num here.
    num1=split(array1[i],array2,"+")              ##Splitting 2nd field further to array2 with delimiter + here.
      for(k=1;k<=num1;k++){                       ##Running for loop to all fields wchih are separated by + here.
        val+=array2[k]                            ##Creating val which keeps on adding value of array2 with index k here.
      }
    array1[i]=(array2[1]!=""?"":"+") val (array2[num1]!=""?"":"+")    ##Assigning val to current array1 value after addition of all items in 2nd field.
    val=""                                        ##Nullifying val here.
  }
  for(o=1;o<=num;o++){                            ##Running a for loop till length of 1st array here.
    value=(value?value ",":"")array1[o]           ##Keep on appending value of array1 with index o to var value here.
  }
  $2=value                                        ##Setting value to 2nd field here.
  value=""                                        ##Nullify var value here.
}
1                                                 ##Mentioning 1 to print all lines here.
' Input_file                                      ##Mentioning Input_file name here.

Upvotes: 3

Related Questions