合并第一列的结果然后将第二列汇总到第一列中每个条目的总计

时间:2013-05-03 03:04:53

标签: linux bash shell awk

我有点Bash新手,所以请在这里忍受我。

我有一个由另一个软件(我无法控制)转储的文本文件,列出每个用户访问某些资源的次数,如下所示:

Jim 109
Bob 94
John 92
Sean 91
Mark 85
Richard 84
Jim  79
Bob  70
John 67
Sean 62
Mark 59
Richard 58
Jim  57
Bob  55
John 49
Sean 48
Mark 46
.
.
.

我的目标是获得这样的输出。

Jim  [Total for Jim]
Bob  [Total for Bob]
John [Total for John]

等等。

每次在软件中运行查询时名称都会更改,因此对每个名称进行静态搜索,然后通过wc进行管道传输无效。

3 个答案:

答案 0 :(得分:6)

这听起来像是awk的工作:)将程序的输出传递给以下awk脚本:

your_program | awk '{a[$1]+=$2}END{for(name in a)print name " " a[name]}'

输出:

Sean 201
Bob 219
Jim 245
Mark 190
Richard 142
John 208

awk脚本本身可以用这种格式更好地解释:

# executed on each line
{
  # 'a' is an array. It will be initialized 
  # as an empty array by awk on it's first usage
  # '$1' contains the first column - the name
  # '$2' contains the second column - the amount
  #
  #  on every line the total score of 'name' 
  #  will be incremented  by 'amount'
  a[$1]+=$2
}
# executed at the end of input
END{
  # print every name and its score
  for(name in a)print name " " a[name]
}

注意,要获得按分数排序的输出,您可以将另一个管道添加到sort -r -k2-r -k2按相反顺序对第二列进行排序:

your_program | awk '{a[$1]+=$2}END{for(n in a)print n" "a[n]}' | sort -r -k2

输出:

Jim 245
Bob 219
John 208
Sean 201
Mark 190
Richard 142

答案 1 :(得分:4)

Pure Bash:

declare -A result                 # an associative array

while read name value; do
  ((result[$name]+=value))
done < "$infile"

for name in ${!result[*]}; do
  printf  "%-10s%10d\n"  $name  ${result[$name]}
done

如果第一个'done'没有从输入文件重定向 这个脚本可以用于管道:

your_program | ./script.sh

并对输出进行排序

your_program | ./script.sh | sort

输出:

Bob              219
Richard          142
Jim              245
Mark             190
John             208
Sean             201

答案 2 :(得分:1)

GNU datamash

datamash -W -s -g1 sum 2 < input.txt

输出:

Bob 219
Jim 245
John    208
Mark    190
Richard 142
Sean    201