【问题标题】:Sorting by number of repetitions按重复次数排序
【发布时间】:2012-01-21 01:25:35
【问题描述】:

我有一个如下所示的文件:

192.168.2.2 150.25.45.7 8080
192.168.12.25 178.25.45.7 50
192.168.2.2 142.55.45.18 369
192.168.489.2 122.25.35.7 8080
192.168.489.2 90.254.45.7 80
192.168.2.2 142.55.45.18 457

所有的数字都是我编的。

我需要根据第一个ip的重复次数对所有这些文件进行排序。所以理想的输出应该是这样的:

192.168.2.2 8080 369 457 3
192.168.489.2 8080 80 2
192.168.12.25 50 1

所以:第一个 ip,与第一个 ip 一致的所有端口,以及重复次数。

我一直在尝试使用 sort 命令和 awk,但我不想做额外的工作,并且可能会错过其他一些简单的解决方案。

有什么想法吗?谢谢:)

【问题讨论】:

  • 称为频率或直方图
  • 我在上面加了一行。我只是想知道我是否缺少一些简单的解决方案。

标签: perl shell awk


【解决方案1】:

Perlish 答案看起来像这样。

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

my %data;

# Store IP address and port number
while (<DATA>) {
  chomp;
  my ($ip, undef, $port) = split;
  push @{$data{$ip}}, $port;
}

# Sort (in reverse) by length of list of ports
for (sort { @{$data{$b}} <=> @{$data{$a}} } keys %data) {
  say "$_ @{$data{$_}} ", scalar @{$data{$_}};
}

__DATA__
192.168.2.2 150.25.45.7 8080
192.168.12.25 178.25.45.7 50
192.168.2.2 142.55.45.18 369
192.168.489.2 122.25.35.7 8080
192.168.489.2 90.254.45.7 80
192.168.2.2 142.55.45.18 457

输出:

192.168.2.2 8080 369 457 3
192.168.489.2 8080 80 2
192.168.12.25 50 1

【讨论】:

【解决方案2】:

一种 Perl 方式:

#!/usr/bin/perl
use strict;
use warnings;

my %repeat;
while(<DATA>) {
    if (/^(\d+(?:.\d+){3})\s\S+\s(\d+)$/) {
        push @{$repeat{$1}}, $2;
    }
}
foreach (sort {@{$repeat{$b}}<=>@{$repeat{$a}}} keys %repeat) {
    my $num = @{$repeat{$_}};
    print "$_ @{$repeat{$_}} $num\n";
}

__DATA__
192.168.2.2 150.25.45.7 8080
192.168.12.25 178.25.45.7 50
192.168.2.2 142.55.45.18 369
192.168.489.2 122.25.35.7 8080
192.168.489.2 90.254.45.7 80
192.168.2.2 142.55.45.18 457

输出:

192.168.2.2 8080 369 457 3
192.168.489.2 8080 80 2
192.168.12.25 50 1

【讨论】:

  • 我认为您错过了按每个源 IP 地址的出现次数对输出进行排序的要求。
  • @davorg:是的,我做到了。更新了答案。
【解决方案3】:

另一个 Perl 解决方案:

#!/usr/bin/perl

use strict;
use warnings;

my %ips;
push @{$ips{$_->[0]}}, $_->[1]+0 for map{[split/ \S+ /]}<DATA>;

for (sort {@{$ips{$b}} <=> @{$ips{$a}}} keys %ips) {
    printf("%s %s %d\n", $_, join(" ", @{$ips{$_}}), 0+@{$ips{$_}});
}

__DATA__
192.168.2.2 150.25.45.7 8080
192.168.12.25 178.25.45.7 50
192.168.2.2 142.55.45.18 369
192.168.489.2 122.25.35.7 8080
192.168.489.2 90.254.45.7 80
192.168.2.2 142.55.45.18 457

输出:

192.168.2.2 8080 369 457 3
192.168.489.2 8080 80 2
192.168.12.25 50 1

【讨论】:

    【解决方案4】:

    这条线应该为你完成这项工作:

    awk '{a[$1]++;b[$1]=b[$1]" "$3}END{for(x in a)print a[x]"\t"x,b[x],a[x]}' input |
    sort -nr|cut -f2-
    

    测试用你的例子

    kent$  cat tt
    192.168.2.2 150.25.45.7 8080
    192.168.12.25 178.25.45.7 50
    192.168.2.2 142.55.45.18 369
    192.168.489.2 122.25.35.7 8080
    192.168.489.2 90.254.45.7 80
    192.168.2.2 142.55.45.18 457
    
    kent$  awk '{a[$1]++;b[$1]=b[$1]" "$3}END{for(x in a)print a[x]"\t"x,b[x],a[x]}' tt |
    sort -nr|cut -f2-
    192.168.2.2  8080 369 457 3
    192.168.489.2  8080 80 2
    192.168.12.25  50 1
    

    【讨论】:

      【解决方案5】:

      这是一个主要依赖于 awk 和 sort 的管道:

      sort -k1 -k3n \
      | awk -F' ' '
          NR==1 { 
            printf("%s ", $1); 
            current = $1 
          } 
          $1 != current { 
            printf(":%d\n%s ", count, $1); 
            current = $1; 
            count = 0 
          } 
          { printf("%d ", $3); count++ } 
          END { printf(":%d\n", count) }' \
      | sort -t':' -k2nr \
      | tr -d':'
      

      【讨论】:

        【解决方案6】:

        GNU awk 4:

        awk 'END {
          PROCINFO["sorted_in"] = "@val_num_desc"
          for (e in ic)
            print e, ip[e], ic[e] 
          }
        {
          ip[$1] = $1 in ip ? ip[$1] OFS $NF : $NF
          ic[$1]++
          }' infile
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-01-23
          • 2017-06-03
          • 2018-04-24
          • 2012-07-06
          • 2014-09-24
          • 2023-01-19
          • 2017-11-01
          • 2013-11-05
          相关资源
          最近更新 更多