查找文本文件中出现次数最多的单词

时间:2018-11-26 07:25:49

标签: unix command-line text-processing

我有一个日志文件,其中记录了因消息错误而失败的cat和sub cat名称。我的目标是找到出现次数最多的类别。

例如日志。

Mon, 26 Nov 2018 07:51:07 +0100 | 164: [ERROR ***] Category ID not found for 'mcat-name1' 'subcat-name1' ref: '073' 
Mon, 26 Nov 2018 07:51:08 +0100 | 278: [ERROR ***] Category ID not found for 'mcat-name2' 'subcat-name2' ref: '020' 

现在,我想确定失败的十大类别。

使用sed:

sed -e 's/\s/\n/g' < file.log | grep ERROR | sort | uniq -c | sort -nr  | head  -10

我收到1636 [ERROR

虽然我一直在寻找出现次数增加后的类别列表。例如

139 category1
23 category 2
...

5 个答案:

答案 0 :(得分:1)

您说您想使用sed进行计数,但是实际上,您正在使用sedgrepsort,{{1} }和uniq。通常,发生这种情况时,您的问题是为head尖叫:

awk

上述解决方案是GNU awk解决方案,因为它利用了非POSIX兼容功能,例如数组遍历的排序(awk 'BEGIN{FS="\047"; PROCINFO["sorted_in"]="@val_num_asc"} /\[ERROR /{c[$2]++} END{for(i in c) { print c[i],i; if(++j == 10) exit } }' file )。字段分隔符设置为PROCINFO),它具有八进制值',因为它假定类别名称在单引号之间。

如果您不使用GNU awk,则可以使用\047sort或自己进行排序。一种方法是:

head

或者只是做:

awk 'BEGIN{FS="\047"; n=10 }
     /\[ERROR /{ c[$2]++ }
     END {
       for (l in c) {
         for (i=1;i<=n;++i) { 
           if (c[l] > c[s[i]]) {
             for(j=n;j>i;--j) s[j]=s[j-1];
             s[i]=l
             break
           }
         }
       }
       for (i=1;i<=n;++i) {
         if (s[i]=="") break
         print c[s[i]], s[i]
       }
     }' file

答案 1 :(得分:0)

您之所以得到1636 [ERROR是因为您将空格字符更改为换行符,然后将ERROR字词变成grep,然后再进行计数。

此:

sed -e 's/\s/\n/g' < file.log | grep ERROR 

给你这个:

[ERROR
[ERROR
[ERROR
[ERROR
[ERROR
[ERROR
... (1630 more)

您需要先grep然后再进行sed(很确定您可以使用sed做得更好,但我只是在谈论命令背后的逻辑):

grep ERROR file.log | sed -e 's/\s/\n/g' | sort | uniq -c | sort -nr | head -10

这可能不是最好的解决方案,因为它计算了ERROR字和其他无用的字,但是您没有在输入文件中提供很多信息。

答案 2 :(得分:0)

假设'Bulgari'是您要提取的类别的示例,请尝试

sed -n "s/.*ERROR.*\] Category '\([^']*\)'.*/\1/p" file.log |
sort | uniq -c | sort -rn | head -n 10

sed命令查找与相当复杂的正则表达式匹配的行并捕获该行的一部分,然后用捕获的子字符串替换匹配项并进行打印(-n选项禁用默认打印操作,因此我们仅打印提取的行)。其余的基本上与您已经拥有的相同。

在正则表达式中,我们查找(行开头,后跟)任何内容(换行符除外),后跟ERROR,之后是] Category ',然后是不包含单引号,然后是结尾的单引号,后跟任何内容。为了用单引号内的捕获字符串替换整个行,需要大量的“任何内容(换行符除外)”。用反斜杠括起来的是表达式。 google以“ backref”作为完整的独家新闻。

您最初的尝试只会提取实际的ERROR字符串,因为您用换行符替换了所有周围的空格(假设您的sed接受了Perl \s的简写,即sed中的标准换行符,而\n在替换中被解释为原义的换行符,这也不是完全标准或可移植的。

答案 3 :(得分:0)

方法是选择错误的类别,并使用sed仅用类别名称替换整行。

尝试一下:

sed -e "s/^.* [[]ERROR .*[]] Category '\([^']*\)' .*$/\1/g" file.log | sort  | uniq -c | sort -nr | head -16

^是该行的开头

\( ... \):此正则表达式中出现的第一个对可以用转义括号括起来的char序列用\1引用,对于第二对等用\2可以引用。

$是该行的结尾。

sed选择一行包含[ERROR和一些字符的行,直到],后跟单词Category,然后在之后(空格)char,直到下一个空格char的任何char序列,都用一对转义的括号选择,然后是直到行末的任何char序列。如果找到这样的行,则将其替换为Category之后的char序列。

答案 4 :(得分:0)

使用Perl

> cat merlin.txt
Mon, 26 Nov 2018 07:51:07 +0100 | 164: [ERROR ***] Category ID not found for 'mcat-name1' 'subcat-name1' ref: '073'
Mon, 26 Nov 2018 07:51:08 +0100 | 278: [ERROR ***] Category ID not found for 'mcat-name2' 'subcat-name2' ref: '020'
Mon, 26 Nov 2018 07:51:21 +0100 | 1232: [ERROR ***] Category ID not found for 'make' 'model' ref: '228239'
> perl -ne ' { s/(.*)Category.*for(.+)ref.*/\2/g and s/(\047\S+\047)/$kv{$1}++/ge if /ERROR/}  END { foreach (sort keys %kv) { print "$_ $kv{$_}\n" } } ' merlin.txt | sort -nr
'subcat-name2' 1
'subcat-name1' 1
'model' 1
'mcat-name2' 1
'mcat-name1' 1
'make' 1
>