Barney Chambers
Barney Chambers

Reputation: 2783

Adding variables on a line Bash

I have a file like this:

Dave 10 20 55
Kate 17 12 1 399
Simon 9 209 1 55 5
Charles 1 2

How do I add these numbers together for each person line by line, with it also working for different amounts of numbers?

So the output would be like this:

Dave 85
Kate 429
etc.

Any direction would be appreciated thanks!

Upvotes: 1

Views: 94

Answers (5)

David W.
David W.

Reputation: 107080

Here's an Awk program. Awk automatically loops through all lines in your file. I added an inner for loop to loop through each field in each line. Since each line has a different number of fields I use the NF variable which is the number of fields on each line. $1 refers to the first field (the name). The for loops from field #2 to the last field. Using $num instead of num refers to the value of that field:

awk '{
    printf $1 "   "
    sum = 0
    for ( num = 2; num <= NF; num++ ) {
        sum += $num
    }
    print sum
}' file.txt

This prints out:

Dave   85
Kate   429
Simon   279
Charles   3

It shouldn't be too hard to get everything to line up.

Upvotes: 1

Michael Jaros
Michael Jaros

Reputation: 4681

I would like to add a Bash-only solution:

while read a b ; do echo "${a} "$(( ${b// /+} )) ; done < numbers.txt

or, expanded:

#!/bin/bash

while read name numbers
do
  echo -n "${name} "
  echo $(( ${numbers// /+} ))
done < numbers.txt

This uses Bash's pattern substitution during parameter expansion to replace blanks with +, and arithmetic expansion to calculate the sum.

Upvotes: 2

5gon12eder
5gon12eder

Reputation: 25439

This kind of task is what AWK is really made for.

The following script – optimized for readability, not minimum number of characters – would do what you have asked for.

/^[[:alpha:]]+([[:space:]]+[[:digit:]]+)*[[:space:]]*$/ {
    name = $1;
    sum = 0;
    for (i = 2; i <= NF; ++i) {
        sum += $i;
    }
    printf("%s %d\n", name, sum);
}

If you save the script as script.awk and your data is in a file named data.txt, you can run awk -f script.awk data.txt and get the result written to standard output.

The logic in the above script is pretty straight forward if you know the basics of AWK. If you don't, I recommend reading The GNU Awk User’s Guide. In a nutshell: ^[[:alpha:]]+([[:space:]]+[[:digit:]]+)*[[:space:]]*$ is a regular expression (using POSIX character classes) that matches a name followed by zero or more numbers. If that pattern is matched, the rule is activated and iterates over all but the first field in the record, summing them up. Finally, the result is printed via the familiar printf.

If you want to write a single shell script, you can inline the AWK script like so:

awk '
  # Here goes the AWK script...
  # It cannot contain a single quote in this case.
' data.txt

Upvotes: 1

Michael Jaros
Michael Jaros

Reputation: 4681

This simple approach uses Bash, tr and bc (assuming your file is called numbers.txt):

#!/bin/bash

while read name numbers
do
  echo -n "${name} "
  tr " " "+" <<< "$numbers" | bc
done < numbers.txt

Each line is separated into name and numbers. After printing name, the blanks are replaced by + with tr, then piped to bc to calculate the sum for that line.

And as a one-liner:

while read a b ; do echo -n "${a} " ; tr " " + <<<"$b" | bc ; done < numbers.txt

Note: This solution is rather slow for large input files because it calls multiple external programs per iteration.

Upvotes: 0

alijandro
alijandro

Reputation: 12167

awk is very good in doing such calculation. Here is another awk solution.

$ awk '{for(i=2;i<=NF;i++)a[$1]+=$i}END{for(j in a)print j, a[j]}' input.txt 
Charles 3
Dave 85
Simon 279
Kate 429

Upvotes: 0

Related Questions