Reputation: 447
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
Reputation: 8406
Using GNU sed
, to wrap some shell script $(( ))
around the sums, and then the dangerous e
val 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
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 digitsmatch($0, r, m)
will be true if the regex matches, portion matched will be available in m
arraym[1] + m[2]
sum the two numbersawk '{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 supportedNote
[:digit:]
should be used inside a character class [[:digit:]]
++
should be +\+
as you intend the second one to match +
literallySee 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
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