Reputation: 2783
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
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
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
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
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
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