【问题标题】:Bash addition by the first column, if data in second column is the same如果第二列中的数据相同,则通过第一列添加 Bash
【发布时间】:2017-11-14 14:34:10
【问题描述】:

我有一个带有分隔符的列表|

40|192.168.1.2|user4
42|192.168.1.25|user2
58|192.168.1.55|user3
118|192.168.1.3|user11
67|192.168.1.25|user2

如您所见,我在42|192.168.1.25|user2 字段和67|192.168.1.25|user2 字段中有相同的ip。我怎样才能在它们之间附加这些线?你能用awk给我一个解决方案吗?可以举几个例子吗?

我需要这样的结果:

40|192.168.1.2|user4
58|192.168.1.55|user3
109|192.168.1.25|user2
118|192.168.1.3|user11

你怎么看,我们已经数完了第一列的数字。

【问题讨论】:

  • 如果ip地址相同,但用户id不同,你想做什么,例如:42|192.168.1.25|user136|192.168.1.25|user9
  • 是的,这是一个问题。我应该只附加 ip
  • 鉴于我在之前的评论中提供的 2 个示例行......您希望输出是什么?显示user1 或显示user9?
  • 显示第一个用户

标签: linux bash shell awk sed


【解决方案1】:

在同一路径上的另一个想法,但允许不同的用户:

awk -F'|' '{c[$2] += $1}u[$2] !~ $3{u[$2] = (u[$2]?u[$2]",":"")$3}END{for(i in c)print c[i],i,u[i]}' OFS='|' input_file

如果有多个用户,他们将用逗号分隔

【讨论】:

    【解决方案2】:

    如果您需要以与 Input_file 相同的顺序输出,那么关注 awk 可能对您有所帮助。

    awk -F"|" '!c[$2,$3]++{val++;v[val]=$2$3} {a[$2,$3]+=$1;b[$2,$3]=$2 FS $3;} END{for(j=1;j<=val;j++){print a[v[j]] FS b[v[j]]}}' SUBSEP=""   Input_file
    

    现在也添加非单线形式的解决方案。

    awk -F"|" '        ##Making field separator as pipe(|) here for all the lines for Input_file.
    !c[$2,$3]++{       ##Checking if array C whose index is $2,$3 is having its first occurrence in array c then do following.
      val++;           ##incrementing variable val value with 1 each time cursor comes here.
      v[val]=$2$3      ##creating an array named v whose index is val and value is $2$3(second field 3rd field).
    }                  ##Closing c array block here now.
    {
      a[$2,$3]+=$1;    ##creating an array named a whose index is $2 $3 and incrementing its value with 1st field value and add in its same index values to get SUM.
      b[$2,$3]=$2 FS $3;##create array b with index of $2$3 and setting its value to $2 FS $3, where FS is field separator.
    }                  ##closing this block here.
    END{               ##Starting awk code END bock here.
      for(j=1;j<=val;j++){ ##starting a for loop here from variable named j value 1 to till value of variable val here.
        print a[v[j]] FS b[v[j]] ##printing value of array a whose index is value of array v with index j, and array b with index of array v with index j here.
    }}
    ' SUBSEP="" Input_file       ##Setting SUBSEP to NULL here and mentioning the Input_file name here.
    

    【讨论】:

    • 男人。很好,它有效。但真的很难理解 =) 无论如何,谢谢。
    • @Piduna,很高兴它对您有所帮助,我现在也添加了非单线形式的解决方案,让我也在那里添加解释并让您知道。
    • 是的,我正在尝试理解您的代码并仅通过第二列(ip 在哪里)来制作它。无论如何谢谢
    • 是的,我做到了awk -F"|" '!c[$2]++{val++;v[val]=$2} {a[$2]+=$1;b[$2]=$2 FS $3;} END{for(j=1;j&lt;=val;j++){print a[v[j]] FS b[v[j]]}}' SUBSEP=""
    • @Piduna,现在请检查我的编辑和解释,如果一切顺利,请告诉我。
    【解决方案3】:

    短 GNU datamash + awk 解决方案:

    datamash -st'|' -g2,3 sum 1 <file | awk -F'|' '{print $3,$1,$2}' OFS='|'
    
    • g2,3 - 按第二个和第三个字段分组(即按IP地址用户ID

    • sum 1 - 对分组记录中的第一个字段值求和


    输出:

    40|192.168.1.2|user4
    109|192.168.1.25|user2
    118|192.168.1.3|user11
    58|192.168.1.55|user3
    

    【讨论】:

      【解决方案4】:

      修改示例数据以包含 IP 地址192.168.1.25 的不同用户:

      $ cat ipfile
      40|192.168.1.2|user4
      42|192.168.1.25|user1      <=== same ip, different user
      58|192.168.1.55|user3
      118|192.168.1.3|user11
      67|192.168.1.25|user9      <=== same ip, different user
      

      还有一个简单的awk 脚本:

      $ awk '
      BEGIN { FS="|" ; OFS="|" }
      { sum[$2]+=$1 ; if (user[$2]=="") { user[$2]=$3 } }
      END { for (idx in sum) { print sum[idx],idx,user[idx] } }
      ' ipfile
      
      58|192.168.1.55|user3
      40|192.168.1.2|user4
      118|192.168.1.3|user11
      109|192.168.1.25|user1     <=== captured first user id
      
      • BEGIN { FS="|" ; OFS="|" } :定义输入输出字段分隔符;在开始时执行一次
      • sum[$2]+=$1 :将字段#1 存储/添加到数组(由ip 地址索引== 字段#2);对数据文件中的每一行执行一次
      • if .... :如果一个用户还没有被存储为一个给定的 IP 地址,那么现在就存储它;这具有保存我们为给定 IP 地址找到的第一个用户 ID 的效果;对数据文件中的每一行执行一次
      • END { for .... / print ...} :遍历数组索引,打印我们的总和、IP 地址和(第一个)用户 ID;最后执行一次

      注意:原始问题中没有提供排序要求;可以根据需要添加排序...

      【讨论】:

        【解决方案5】:

        awk 来救援!

        $ awk 'BEGIN {FS=OFS="|"} 
                     {a[$2 FS $3]+=$1} 
               END   {for(k in a) print a[k],k}' file | sort -n
        
        40|192.168.1.2|user4
        58|192.168.1.55|user3
        109|192.168.1.25|user2
        118|192.168.1.3|user11
        

        如果 user* 不是键的一部分并且您想要捕获第一个值

        $ awk 'BEGIN {FS=OFS="|"} 
                     {c[$2]+=$1; 
                      if(!($2 in u)) u[$2]=$3}     # capture first user
               END   {for(k in c) print c[k],k,u[k]}' file | sort -n
        

        这与@markp 的答案几乎相同。

        【讨论】:

          猜你喜欢
          • 2013-03-03
          • 2018-07-22
          • 2018-10-07
          • 2021-01-30
          • 1970-01-01
          • 2017-06-17
          • 1970-01-01
          • 2022-01-17
          • 1970-01-01
          相关资源
          最近更新 更多