如何从一个文件中生成多个计数,而无需多次重读?

时间:2019-06-20 16:27:04

标签: bash grep

我的HTTP访问日志文件很大,我正在尝试为特定查询字符串生成每小时计数。显然,正确的解决方案是将所有内容都转储为splunk或graylog或其他内容,但我目前无法一次性完成所有设置。

快捷方式是:

for hour in 0{0..9} {10..23}
do
  grep $QUERY $FILE | egrep -c "^\S* $hour:"
  # or, alternately
  # egrep -c "^\S* $hour:.*$QUERY" $FILE
  # not sure which one's better
done

但是这些文件平均需要15-20M行,我真的不想解析每个文件24次。一次性分析文件并计算每个$hour实例的效率会更高。有什么办法可以做到这一点?

3 个答案:

答案 0 :(得分:1)

您可以要求grep用-o输出每行的匹配部分,然后使用uniq -c来计数结果:

grep "$QUERY" "$FILE" | grep -o "^\S* [0-2][0-9]:" | sed 's/^\S* //' | uniq -c

这里的sed命令仅保留两位数的小时和冒号,如果需要,您还可以使用另一个sed表达式将其删除。

注意事项:此解决方案可与GNU grep和GNU sed一起使用,并且在没有日志条目的情况下,不会产生任何输出,而不是“ 0”。感谢@EdMorton在注释中指出了这些问题,以及上面的答案中已解决的其他问题。

答案 1 :(得分:1)

假设时间戳显示在2位数小时之前有一个空格,然后是一个冒号

digraph "a" {
a -> b
}

这将创建24个文件。

需要3 arg形式的match()GNU awk

答案 2 :(得分:0)

这可能正是您真正需要的,使用GNU awk作为match()的第三个arg,并假设您的输入看起来像什么,QUERY变量可能包含什么以及输出应该是什么样子:

awk -v query="$QUERY" '
    match($0, " ([0-9][0-9]):.*"query, a) { cnt[a[1]+0]++ }
    END {
        for (hr=0; hr<=23; hr++) {
           printf "%02d = %d\n", hr, cnt[hr]
        }
    }
' "$FILE"

对于未导出的shell变量,请不要真正使用大写字母-参见Correct Bash and shell script variable capitalization