【问题标题】:Find the most common line in a file in bash在 bash 中查找文件中最常见的行
【发布时间】:2015-06-01 23:34:57
【问题描述】:

我有一个字符串文件:

string-string-123
string-string-123
string-string-123
string-string-12345
string-string-12345
string-string-12345-123

如何检索 bash (string-string-123) 中最常见的行?

【问题讨论】:

标签: linux bash text-processing


【解决方案1】:

您可以将sortuniq 一起使用

sort file | uniq -c | sort -n -r

【讨论】:

    【解决方案2】:

    您可以使用 awk 来执行此操作:

    awk '{++a[$0]}END{for(i in a)if(a[i]>max){max=a[i];k=i}print k}' file
    

    数组a 保存每一行的计数。读取文件后,我们循环遍历它并找到具有最大计数的行。

    或者,您可以通过在文件处理期间分配行来跳过END 块中的循环:

    awk 'max < ++c[$0] {max = c[$0]; line = $0} END {print line}' file
    

    感谢glenn jackman 提供这个有用的建议。


    正确地指出,以上两种方法在平局的情况下只会打印出最常出现的行之一。以下版本将打印出所有最常出现的行:

    awk 'max<++c[$0] {max=c[$0]} END {for(i in c)if(c[i]==max)print i}' file
    

    【讨论】:

    • 为了简单起见,将“max”逻辑移出 END 块:awk '{if (max &lt; ++c[$0]) {max = c[$0]; line = $0}} END {print line}'
    • 这是一个优雅的解决方案,但请注意,如果最常出现的行有 ties,则您的解决方案将只打印其中的 one,并且它不会很明显。
    • @mklement0 这是一个公平的观点。我添加了另一个版本,可以打印所有这些。
    • 感谢更新 - 做得很好。唯一剩下的问题是这种方法可能不适用于大型输入文件,因为您将 all distinct 输入行读入内存,这可能是输入中有大量不同行的问题。从好的方面来说,您的方法比sort / uniq 方法快得多
    • 另一种方法是使用asort awk '{b[a[$0]++]=$0}END{asort(b);print b[1]}'
    【解决方案3】:
    • Tom Fenech's elegant awk answer 工作得很好[在打印所有在出现平局时最常出现的行的修正版本中]。
      但是,它可能不适合大文件,因为所有 distinct 输入行都存储在内存中的关联数组中,如果有很多非重复行,这可能会出现问题;也就是说,它比下面讨论的方法

    • Grzegorz Żur's answer 优雅地结合了多个实用程序以隐式产生所需的结果,但是:

      • 所有不同的行都被打印出来(首先是最高频率)
      • 输出行以出现次数为前缀(这实际上可能是可取的)。

    虽然您可以通过管道将Grzegorz Żur's answer 传递到head 以限制显示的行数,但您不能假设一般固定行数。

    基于 Grzegorz 的回答,这是一个通用的解决方案,它显示所有最常出现的行 - 无论有多少行 - 并且它们

    sort file | uniq -c | sort -n -r | awk 'NR==1 {prev=$1} $1!=prev {exit} 1'
    

    如果您不希望输出行以出现次数为前缀:

    sort file | uniq -c | sort -n -r | awk 'NR==1 {prev=$1} $1!=prev {exit} 1' | 
      sed 's/^ *[0-9]\{1,\} //'
    

    Grzegorz Żur's answer的解释:

    • uniq -c 输出一组唯一输入行,以各自的出现次数为前缀 (-c),后跟一个空格。
    • sort -n -r 然后按数字 (-n) 降序 (-r) 对结果行进行排序,以便最常出现的行位于顶部。
      • 注意sort,如果未指定-k,一般会尝试按整个输入行排序,但-n只会导致被识别的最长前缀作为用于排序的整数,这正是这里所需要的。

    解释我的awk 命令:

    • NR==1 {prev=$1} 将第一个空格分隔字段 ($1) 存储在变量 prev 中,用于 first 输入行 (NR==1)
    • $1!=prev {exit} 终止处理,如果第一个空格分隔的字段与前一行的不同 - 这意味着已到达非最上面的行,不再需要打印行。
    • 1{ print } 的简写,表示手头的输入行应按原样打印。

    解释我的sed 命令:

    • ^ *[0-9]\{1,\} 匹配每个输出行的数字前缀(表示出现次数),如(最初)由 uniq -c 生成的
    • 应用s/...// 意味着前缀被替换为空字符串,即有效地移除

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-30
      • 2014-09-24
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 2013-02-21
      相关资源
      最近更新 更多