在文本文件中查找出现次数最多的单词
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
进行计数,但实际上,你有一个完整的管道 sed
、grep
、sort
、uniq
和 head
。通常,发生这种情况时,您的问题是 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,您可以使用 sort
和 head
或自己进行排序。一种方式是:
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
>
我有一个日志文件,用于记录因消息错误而失败的猫和子猫名称。我的目标是找到出现次数最多的类别。
例如日志:
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
进行计数,但实际上,你有一个完整的管道 sed
、grep
、sort
、uniq
和 head
。通常,发生这种情况时,您的问题是 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,您可以使用 sort
和 head
或自己进行排序。一种方式是:
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
>