【问题标题】:Combine results of column one Then sum column 2 to list total for each entry in column one合并第一列的结果然后对第 2 列求和以列出第一列中每个条目的总计
【发布时间】:2013-05-03 03:04:52
【问题描述】:

我是 Bash 新手,所以请多多包涵。

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

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

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

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

等等。

每次我在软件中运行查询时名称都会更改,因此静态搜索每个名称然后通过 wc 管道没有帮助。

【问题讨论】:

    标签: linux bash shell awk


    【解决方案1】:

    这听起来像是 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
    

    【讨论】:

    • 这就像一个冠军。谢谢!我走了很长的路,将输出管道传输到 awk '{print $2}',将其读入一个变量,然后尝试对变量求和并列出结果。非常感谢!
    • 再次感谢您的时间、耐心、解释和帮助。我希望有一天我能回报这个人情。这件事情让我感到很快乐。我乞求因为使用 awk 的错误方法而感到沮丧,然后通过尝试重新发明轮子来进一步加剧问题,使用现在似乎不必要的嵌套循环试图对事件求和。在我放弃之前几乎浪费了几个小时。非常感谢。言语无法解释我的感激之情。
    • 别担心! :) 回答这个问题真的很有趣。我会说我最多是 awk 中级,喜欢通过在这里解决问题来学习更多。 awk 真的很酷,就像 UNIX/Linux 命令行中可用的许多工具一样。将它们连接在一起,您可以做您无法想象的事情 - 一条线! :)
    【解决方案2】:

    纯猛击:

    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
    

    【讨论】:

    • @fgm。感谢您的 bash 解决方案。这肯定会帮助我学习更多 bash,因为我还是个新手。谢谢大家,我真的很感谢在这方面的所有帮助。
    【解决方案3】:

    GNU datamash:

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

    输出:

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

    【讨论】:

    • 我不知道 datamash。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-11
    • 2018-11-02
    • 2021-11-27
    • 2016-10-20
    相关资源
    最近更新 更多