Shell 用于对与名称关联的列求和的脚本

Shell script to sum columns associated with a name

我有一个文件,其中第 1 列有数千个数字,这些数字的每个序列都与一个人相关联。有人知道我如何创建一个 shell 脚本来为那个特定的人计算第 1 列的总和吗,例如:

John is 10+20+30+50 = 110

脚本的输出将是:John 110 依此类推..

我试过 while、for 等,但我无法将总和与人联系起来 :(

文件示例:

10 John
20 John
30 John
50 John
10 Paul
10 Paul
20 Paul
20 Paul
20 Robert
30 Robert
30 Robert 
60 Robert 
80 Robert
40 Robert
40 Robert
40 Robert
15 Mike
30 Mike
awk '{ map[]+= } END { for (i in map) { print i" "map[i] } }' file

使用 awk 创建一个数组,将名称作为第一个索引,每个名称的值总计 运行。最后,打印姓名和总数。

非常感谢 Raman,它成功了...您是否碰巧知道是否可以对同一个 awk 执行计算以获得每个计算的平均值?例如,约翰是 10+20+30+50 = 110, 110 / 4 = 27

假设:

  • 数据位于名为 numbers.dat
  • 的文件中
  • 我们会将总数和计数存储在数组中,但计算平均值只是为了显示(OP 可以决定平均值是否也应该存储在数组中)

一个 bash 解决方案使用一对关联数组来跟踪我们的数字:

unset      total count
declare -A total count

while read -r number name
do
    (( total[${name}] += $number))
    (( count[${name}] ++ ))
done < numbers.dat

typeset -p total count

这会生成:

declare -A total=([Mike]="45" [Robert]="340" [John]="110" [Paul]="60" )
declare -A count=([Mike]="2" [Robert]="8" [John]="4" [Paul]="4" )

如果我们想要基于整数的平均值(即没有小数位):

for i in ${!total[@]}
do
   printf "%-10s %5d / %-5d = %5d\n" "${i}" "${total[${i}]}" "${count[${i}]}" $(( ${total[${i}]} / ${count[${i}]} ))
done

这会生成:

Mike          45 / 2     =    22
Robert       340 / 8     =    42
John         110 / 4     =    27
Paul          60 / 4     =    15

如果我们希望平均值包括 2 个小数位:

for i in ${!total[@]}
do
   printf "%-10s %5d / %-5d = %5.2f\n" "${i}" "${total[${i}]}" "${count[${i}]}" $( bc <<< "scale=2;${total[${i}]} / ${count[${i}]}" )
done

这会生成:

Mike          45 / 2     = 22.50
Robert       340 / 8     = 42.50
John         110 / 4     = 27.50
Paul          60 / 4     = 15.00

按名称排序的输出:

for i in ${!total[@]}
do
   printf "%-10s %5d / %-5d = %5.2f\n" "${i}" "${total[${i}]}" "${count[${i}]}" $( bc <<< "scale=2;${total[${i}]} / ${count[${i}]}" )
done | sort

这会生成:

John         110 / 4     = 27.50
Mike          45 / 2     = 22.50
Paul          60 / 4     = 15.00
Robert       340 / 8     = 42.50

一个 awk 解决方案,打印平均值到小数点后 2 位并按名称排序输出:

awk '
    { total[]+=
      count[]++
    }
END { PROCINFO["sorted_in"]="@ind_str_asc"
      for ( i in total )
          printf "%-10s %5d / %-5d = %5.2f\n", i, total[i], count[i], total[i]/count[i]
    }
' numbers.dat

这会生成:

John         110 / 4     = 27.50
Mike          45 / 2     = 22.50
Paul          60 / 4     = 15.00
Robert       340 / 8     = 42.50