在bash中查找文件中最常见的行

Ale*_*lex 7 linux bash text-processing

我有一个字符串文件:

string-string-123
string-string-123
string-string-123
string-string-12345
string-string-12345
string-string-12345-123
Run Code Online (Sandbox Code Playgroud)

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

Grz*_*Żur 16

你可以用sortuniq

sort file | uniq -c | sort -n -r
Run Code Online (Sandbox Code Playgroud)


Tom*_*ech 5

你可以使用awk来做到这一点:

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

该数组a保持每行的计数.读取文件后,我们遍历它并找到具有最大计数的行.

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

awk 'max < ++c[$0] {max = c[$0]; line = $0} END {print line}' file
Run Code Online (Sandbox Code Playgroud)

谢格伦杰克曼提出这个有用的建议.


正确地指出,上述两种方法仅打印出平局情况下最常出现的一条线.以下版本将打印出所有最常出现的行:

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

  • 将"max"逻辑移出END块以获得简单性:`awk'{if(max <++ c [$ 0]){max = c [$ 0]; line = $ 0}} END {print line}'` (2认同)

mkl*_*nt0 5

    \n
  • 汤姆·费内奇(Tom Fenech)优雅的awk答案非常有效[在修正版本中,在出现平局时打印所有最常出现的行]。
    \n但是,它可能不适合大文件,因为所有不同的输入行都存储在内存中的关联数组中,如果有许多非重复行,这可能会成为问题;也就是说,它比下面讨论的方法要快得多。

  • \n
  • Grzegorz \xc5\xbbur\ 的答案优雅地隐式地结合了多个实用程序产生所需的结果,但是:

    \n\n
      \n
    • 全部打印
    • \n
    • 输出行以其出现次数为前缀(这实际上可能是理想的)。
    • \n
  • \n
\n\n

虽然您可以通过管道传递Grzegorz \xc5\xbbur\ 的答案head限制显示的行数,但您不能假设固定的限制显示的行数,但通常

\n\n

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

\n\n
sort file | uniq -c | sort -n -r | awk \'NR==1 {prev=$1} $1!=prev {exit} 1\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

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

Grzegorz \xc5\xbbur\ 的回答的解释

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

我的命令的解释awk

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

我的命令的解释sed

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