6

我目前正在运行一个 awk 脚本来处理一个大的(8.1GB)访问日志文件,而且它需要永远完成。在 20 分钟内,它写入了我期望它写入的 (1000 +- 500)MB 的 14MB,我想知道我是否可以以某种方式更快地处理它。

这是 awk 脚本:

#!/bin/bash

awk '{t=$4" "$5; gsub("[\[\]\/]","",t); sub(":"," ",t);printf("%s,",$1);system("date -d \""t"\" +%s");}' $1

编辑:

对于非 awker,脚本读取每一行,获取日期信息,将其修改为实用程序date可识别的格式,并调用它以将日期表示为自 1970 年以来的秒数,最后将其作为 .csv 文件的一行返回,以及IP。

示例输入: 189.5.56.113 - - [22/Jan/2010:05:54:55 +0100] "GET (...)"

返回输出: 189.5.56.113,124237889

4

5 回答 5

13

@OP,您的脚本很慢,主要是因为对文件中的每一行都过度调用系统日期命令,而且它也是一个大文件(以 GB 为单位)。如果您有 gawk,请使用其内部 mktime() 命令将日期转换为纪元秒

awk 'BEGIN{
   m=split("Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec",d,"|")
   for(o=1;o<=m;o++){
      date[d[o]]=sprintf("%02d",o)
    }
}
{
    gsub(/\[/,"",$4); gsub(":","/",$4); gsub(/\]/,"",$5)
    n=split($4, DATE,"/")
    day=DATE[1]
    mth=DATE[2]
    year=DATE[3]
    hr=DATE[4]
    min=DATE[5]
    sec=DATE[6]
    MKTIME= mktime(year" "date[mth]" "day" "hr" "min" "sec)
    print $1,MKTIME

}' file

输出

$ more file
189.5.56.113 - - [22/Jan/2010:05:54:55 +0100] "GET (...)"
$ ./shell.sh    
189.5.56.113 1264110895
于 2010-01-22T08:28:25.833 回答
2

如果你真的需要它更快,你可以做我所做的。我使用 Ragel 重写了一个 Apache 日志文件分析器。Ragel 允许您将正则表达式与 C 代码混合。正则表达式被转换成非常高效的 C 代码,然后被编译。不幸的是,这要求您非常熟悉用 C 编写代码。我不再拥有这个分析器。它在 1 或 2 秒内处理了 1 GB 的 Apache 访问日志。

从 awk 语句中删除不必要的 printfs 并用更简单的东西替换它们可能会取得有限的成功。

于 2010-01-22T04:36:27.223 回答
2

如果您正在使用gawk,您可以将日期和时间转换为mktimegawk函数)可以理解的格式。它将为您提供您现在使用的相同时间戳,并为您节省重复system()调用的开销。

于 2010-01-22T05:45:26.270 回答
2

这个小 Python 脚本在我的机器上大约 3 分钟内处理了价值约 400MB 的示例行副本,产生了约 200MB 的输出(请记住,您的示例行很短,所以这是一个障碍):

import time

src = open('x.log', 'r')
dest = open('x.csv', 'w')

for line in src:
    ip = line[:line.index(' ')]
    date = line[line.index('[') + 1:line.index(']') - 6]
    t = time.mktime(time.strptime(date, '%d/%b/%Y:%X'))
    dest.write(ip)
    dest.write(',')
    dest.write(str(int(t)))
    dest.write('\n')

src.close()
dest.close()

一个小问题是它不处理时区(strptime() 问题),但您可以对其进行硬编码或添加一些额外内容来处理它。

但老实说,像这样简单的东西应该很容易用 C 重写。

于 2010-01-22T05:51:16.867 回答
1
gawk '{
    dt=substr($4,2,11); 
    gsub(/\//," ",dt); 
    "date -d \""dt"\" +%s"|getline ts; 
    print $1, ts
}' yourfile
于 2013-06-07T13:51:59.247 回答