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

Find most occurring words in text file

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

例如日志:

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' 

现在我想确定失败的前 10 个类别。

使用 sed:

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

我收到 1636 [错误

当我在寻找按发生次数排序的类别列表时。例如

139 category1
23 category 2
...

你得到1636 [ERROR是因为你把space字符改成了换行符,然后你grep了ERROR这个词,然后你算了。

这个:

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 和其他无用的单词,但您没有给我们很多关于输入文件的信息。

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

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

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

在正则表达式中,我们寻找(行首后跟)任何内容(换行符除外)后跟 ERROR ,然后是 ] Category ' ,然后是不包含的字符串包含单引号,然后是结束单引号,然后是任何内容。为了用单引号内捕获的字符串替换整行,需要大量的 "anything (except newline)" 。反斜杠括号是捕获表达式的内容; google "backref" 完整独家报道。

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

方法是 select 错误的类别,并使用 sed.

仅用类别名称替换整行

试试这个:

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

^ 是行首

\( ... \) :对于出现在正则表达式中的第一对,用 </code> 引用这个转义括号中的字符序列,对于第二对,用 <code> 引用等等。

$是行尾。

sed select 一行包含 [ERROR 和一些字符,直到 ],后面跟 Category,然后是 Category </code> (space) 字符,任何字符序列,直到下一个 space 字符,是 selected 与一对转义括号,后跟直到行尾的任何字符序列。如果找到这样的一行,则将其替换为 <code>Category.

之后的字符序列

你说你想用 sed 进行计数,但实际上,你有一个完整的管道 sedgrepsortuniqhead。通常,发生这种情况时,您的问题是 awk:

awk 'BEGIN{FS="7"; PROCINFO["sorted_in"]="@val_num_asc"}
     /\[ERROR /{c[]++}
     END{for(i in c) { print c[i],i; if(++j == 10) exit } }' file

上述解决方案是 GNU awk 解决方案,因为它使用了不符合 POSIX 的功能,例如数组遍历排序 (PROCINFO)。字段分隔符设置为 <单引号> ('),它具有八进制值 7,因为它假定类别名称在单引号之间。

如果您不使用 GNU awk,您可以使用 sorthead 或自己进行排序。一种方式是:

awk 'BEGIN{FS="7"; n=10 }
     /\[ERROR /{ c[]++ }
     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

或者只是做:

awk 'BEGIN{FS="7"}
     /\[ERROR /{c[]++}
     END{for(i in c) { print c[i],i; if(++j == 10) exit } }' file \
| sort -nr | head -10

使用 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.*//g and s/(7\S+7)/$kv{}++/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
>