Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing `sumproduct` in UNIX shell

Tags:

bash

shell

unix

awk

I have some output from a script thescript which reads:

202 1 0 1 0 0 0

Now I want to selectively sum this number with awk, depending on the value of a ${SUM_MASK}:

SUM_MASK=1,1,0,0,0,0,0

I would like to have something like:

thescript | awk <SOMETHING>

where the each number output of thescript gets multiplied by the corresponding number in ${SUM_MASK}, obtaining:

203

as result of: 203 = 202 * 1 + 1 * 1 + 0 * 0 + 1 * 0 + 0 * 0 + 0 * 0 + 0 * 0

This would be similar to the sumproduct function in spreadsheet software.

The following code snipets do the trick, but I would like to avoid using process substitution:

SUM_MASK="1,1,0,0,0,0,0"; paste <(thescript) <(echo ${SUM_MASK} | tr ',' '\n') | awk '{ SUM += $1 * $2 } END { print SUM }'

and named pipes:

SUM_MASK="1,1,0,0,0,0,0"; mkfifo fA; mkfifo fB; thescript > fA & echo ${SUM_MASK} | tr ',' '\n' > fB & paste fA fB | awk '{ SUM += $1 * $2 } END { print SUM }' > result.text; rm -f fA fB

how could I achieve that?

like image 905
norok2 Avatar asked Jan 05 '23 23:01

norok2


2 Answers

echo "202 1 0 1 0 0 0" |
awk -v summask="1,1,0,0,0,0,0" '
    BEGIN {split(summask, mask, /,/)}
    {   sumproduct=0
        for (i=1; i<=NF; i++) {
            sumproduct +=  $i * mask[i]
        }
        print sumproduct
    }
'
203
like image 147
glenn jackman Avatar answered Jan 12 '23 10:01

glenn jackman


There's no need for external tools such as awk here -- bash is capable of resolving this with built-in capabilities only. Consider the below implementation as a function:

sumproduct() {
  local -a sum_inputs sum_mask
  local idx result

  # read your sum_inputs into an array from stdin
  IFS=', ' read -r -a sum_inputs # this could be <<<"$1" to use the first argument

  # and your sum_mask from the like-named variable
  IFS=', ' read -r -a sum_mask <<<"$SUM_MASK" # or <<<"$2" for the second argument

  # ...iterate over array elements in sum_inputs; find the corresponding sum_mask; math.
  result=0
  for idx in "${!sum_inputs[@]}"; do
    (( result += ${sum_mask[$idx]} * ${sum_inputs[$idx]} ))
  done
  echo "$result"
}

To test this:

echo "202 1 0 1 0 0 0" | SUM_MASK=1,1,0,0,0,0,0 sumproduct

...correctly yields:

203
like image 27
Charles Duffy Avatar answered Jan 12 '23 09:01

Charles Duffy