# how to perform mathematical operations on numbers in a file using perl or awk?

I have a fileA.txt:

RS0255_RS0083:115,124,129,141,143,168,170,180
RS0343_RS0083:112,113,163,175,181
RS0343_RS0255:94,101,107,164,179,183


I would like to perform mathematical operations on the numbers after : For example, I want to add 10 to each number: Output:

RS0255_RS0083:125,134,139,151,153,178,180,190
RS0343_RS0083:122,123,173,185,191
RS0343_RS0255:104,111,117,174,189,193


I know how to do it in R but how to do this mathematical operation on numbers in a file in perl or awk?

• "in linux" doesn't really make sense. What programming language do you want to use? python? perl? awk? C? Go? Ruby? Something else? – roaima Feb 14 at 16:40
• I am afraid this is way too broad. There are dozens of tools that can be used but it depends on exactly what you need. – terdon Feb 14 at 16:40
• yeah I meant perl or awk, sorry – Paolo Lorenzini Feb 14 at 16:47
• – Quasímodo Feb 17 at 14:32

There are quite literally dozens of tools you can use for different manipulation of text files. For the specific case you mention, I would probably use perl:

$perl -pe 's/\b(\d+)\b/$1 + 10/ge' fileA.txt
RS0255_RS0083:125,134,139,151,153,178,180,190
RS0343_RS0083:122,123,173,185,191
RS0343_RS0255:104,111,117,174,189,193

• I tried multiplication, addition and subtraction, all ok, for the division how to do? let's suppose I want divide by 10? – Paolo Lorenzini Feb 14 at 17:00
• @PaoloLorenzini, I'd use a different delimiter for s/pattern/repl/flags like s:pattern:repl:flags or s{pattern}{repl}flags – Stéphane Chazelas Feb 14 at 17:01
• @PaoloLorenzini The division operator (/) happens to be equal to the delimiter used in the substitute perl command (s/.../.../). To be able to to use that symbol in the replacement part, either change the delimiter (s#...#...#) or escape the character (\/). Either perl -pe 's#\b(\d+)\b#$1/10#ge' file or perl -pe 's/\b(\d+)\b/$1\/10/ge' file – Isaac Feb 14 at 17:25
gawk '$0+0 ==$0 {$0 += 10} {ORS = RT} 1' RS='[:,\n]' file  An awk answer: Parse each line as two :-delimited fields. Use split() on the second field to split it into fields on the commas, and modify the split-up fields in a loop, creating a new output record. Then output the new record with commas as delimiters, and with the original first field as a "prefix". awk -F : ' BEGIN { OFS = "," } { prefix =$1
nf = split($2,a,",")$0 = ""

for (i = 1; i <= nf; ++i)
$i = a[i] + 10 printf "%s:%s\n", prefix,$0
}' fileA.txt


The output would be

RS0255_RS0083:125,134,139,151,153,178,180,190
RS0343_RS0083:122,123,173,185,191
RS0343_RS0255:104,111,117,174,189,193


More compact presentation of the above code (for those of you that thinks one-lines are somehow "better"):

awk -F: '{p=$1;n=split($2,a,",");$0="";for(i=1;i<n;++i)$i=a[i]+10;printf "%s:%s\n",p,$0}' OFS=, fileA.txt  Older answer, which is not as "nice" as the above one: Using awk and interpreting each line as a list of fields delimited by : or ,, adding 10 to the 2nd field onward on each line: awk -F '[:,]' 'BEGIN { OFS="," } { for (i = 2; i <= NF; ++i)$i += 10 }; 1' fileA.txt


This would give you

RS0255_RS0083,125,134,139,151,153,178,180,190
RS0343_RS0083,122,123,173,185,191
RS0343_RS0255,104,111,117,174,189,193


To change the first comma on each line back to a :, use sed 's/,/:/':

awk -F '[:,]' 'BEGIN { OFS="," } { for (i = 2; i <= NF; ++i) $i += 10 }; 1' fileA.txt | sed 's/,/:/'  awk -F, -v OFS=, '{ split($1, tmp, ":");
$1=tmp[1] ":" tmp[2]+10; for(i=2; i<=NF; i++ )$i+=10;
}1' infile


I propose this awk:

$awk -F':|,' 'BEGIN { OFS="" }$1 = $1":" { OFS=""; for(i=2; i<=NF; i++) {$i = $i + 10; if ( i != NF ) {$i = $i"," } }; }1' file RS0255_RS0083:125,134,139,151,153,178,180,190 RS0343_RS0083:122,123,173,185,191 RS0343_RS0255:104,111,117,174,189,193  Using awk with a progressive slicing around comma we recreate fields dynamically.  awk -F: -v OFS=, ' { f1=$1 FS; f2=$2 OFS n=0;$0=""
while (p=index(f2, OFS)) {
$(++n) = 10 + substr(f2,1,p-1) f2 = substr(f2, p+1) }$1=f1 $1 }1 ' fileA.txt  Output: RS0255_RS0083:125,134,139,151,153,178,180,190 RS0343_RS0083:122,123,173,185,191 RS0343_RS0255:104,111,117,174,189,193  perl -pe '/:/g; s/\G(\d+)(\D)/($1+10).$2/ge; 'fileA.txt #alternatively perl -ne ' print !$|-- ? $_ : s/\d+/$&+10/reg
for /(.*:)(.*)/s;
' fileA.txt