# ADD or SUB numbers based on op-code (directive) in the line

How can I write an `awk` script that can add or subtract based on what the line in the input file is asking?  I figured out how to do either only addition or subtraction, but not either/or in one script.

For example, I would want this input:

``````ADD 5,10,20
SUB 30,5,20
``````

to produce this output:

``````35
-55
``````

This is what I have written so far:

``````#!/bin/awk
BEGIN {
FS=","
}

{
for(i=1;i<=NF;i++)
sum+=\$i;
print sum; sum=0}
``````

but, obviously, it does addition only, and it doesn't even get all the numbers.

• Is the two-lines "input" example you showed one file with two lines, or is it two files with one line each? Can the input files have more than one line? Do you want to process the values per-line, or actually produce a grand total at end-of-file? Sep 16 at 10:38
• based on what condition subtraction result of these 3numbers `30,5,20` become -55? it's not you want `30-5-20` will be `5`? or `-1,-2,-3` will be `-1-(-2)-(-3)=4`? Sep 16 at 18:05

Some tips:

• use either space or comma as the field separator: `FS = "[ ,]"`
• then add up the numbers like you do, except start with `i = 2`
• if the first field is `SUB`, multiply the sum by -1

Here's one way of implementing glenn jackman's algorithm, using an associative array to hold the multiplication values (1 or -1):

``````\$ awk -F ',|[[:blank:]]+' '
BEGIN {
mult["SUB"] = -1;
};

sum = 0;
for (i=2; i <= NF; i++) { sum += \$i };
print sum * mult[\$1];
}' input.txt
35
-55
``````

This also provides the beginnings of a framework to add other keywords - e.g. "MULT" or "DIV" or "EXP" (for these, you would have `sum=\$2` and start the for loop with `i=3` instead of `i=2`).

``````\$ awk -F'[ ,]' '{sum=0; for (i=2; i<=NF; i++) sum+=\$i; print (\$1=="ADD" ? 1 : -1) * sum}' file
35
-55
``````

If you had to support more operations than just ADD and SUB, e.g.:

``````\$ cat file
SUB 30,5,20
MULT 2,3,4
DIV 20,2,2
``````

you could use GNU awk and define functions for every op you want to support and call them indirectly:

``````\$ cat tst.awk
function ADD(n)  { sum += n }
function SUB(n)  { sum -= n }
function DIV(n)  { if (n==0) { printf "%s[%d]: Divide by zero error in %s\n", FILENAME, FNR, \$0 | "cat>&2"; exit 1 } sum /= n }
function MULT(n) { sum *= n }

BEGIN { FS="[ ,]" }
{
op = \$1
sum = (op=="SUB" ? -1 : 1) * \$2
for (i=3; i<=NF; i++) {
@op(\$i)
}
print sum
}
``````

``````\$ awk -f tst.awk file
35
-55
24
5
``````

I had to special-case "SUB" to get the output you want from the input you provided for that as it gets treated differently from the rest (i.e. all numbers subtracted from zero rather than all numbers added/multiplied/divided by each other). If the first number on the SUB line was `-30` or `0` instead of `30` then all operations could be treated exactly the same way, e.g.:

``````\$ cat file
SUB 0,30,5,20
MULT 2,3,4
DIV 20,2,2
``````

``````\$ cat tst.awk
function ADD(n)  { sum += n }
function SUB(n)  { sum -= n }
function DIV(n)  { if (n==0) { printf "%s[%d]: Divide by zero error in %s\n", FILENAME, FNR, \$0 | "cat>&2"; exit 1 } sum /= n }
function MULT(n) { sum *= n }

BEGIN { FS="[ ,]" }
{
op = \$1
sum = \$2
for (i=3; i<=NF; i++) {
@op(\$i)
}
print sum
}
``````

``````\$ awk -f tst.awk file
35
-55
24
5
``````