# 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
``````