3

我有点 Bash 新手,所以请在这里忍耐一下。

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

吉姆 109
鲍勃 94
约翰 92
肖恩 91
马克 85
理查德 84
吉姆 79
鲍勃 70
约翰 67
肖恩 62
马克 59
理查德 58
吉姆 57
鲍勃 55
约翰 49
肖恩 48
马克 46
.
.
.

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

吉姆 [吉姆的总数]
Bob [Bob 的总数]
约翰 [约翰的总数]

等等。

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

4

3 回答 3

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
于 2013-05-03T03:13:58.650 回答
4

纯重击:

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

如果第一个“完成”没有来自输入文件的重定向,则此脚本可以与管道一起使用:

your_program | ./script.sh

并对输出进行排序

your_program | ./script.sh | sort

输出:

Bob              219
Richard          142
Jim              245
Mark             190
John             208
Sean             201
于 2013-05-03T08:06:07.053 回答
1

GNU datamash

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

输出:

Bob 219
Jim 245
John    208
Mark    190
Richard 142
Sean    201
于 2017-04-04T06:45:25.817 回答