从日志文件中使用awk、getline和bash提取指定时间范围内的数据

3

我正在寻找解析日志文件的方法,并在这个链接中找到了我需要的内容extract data from log file in specified range of time

但最有用的答案(由@Kent发布):

# this variable you could customize, important is convert to seconds. 
# e.g 5days=$((5*24*3600))
x=$((5*60))   #here we take 5 mins as example

# this line get the timestamp in seconds of last line of your logfile
last=$(tail -n1 logFile|awk -F'[][]' '{ gsub(/\//," ",$2); sub(/:/," ",$2); "date +%s -d \""$2"\""|getline d; print d;}' )

#this awk will give you lines you needs:
awk -F'[][]' -v last=$last -v x=$x '{ gsub(/\//," ",$2); sub(/:/," ",$2); "date +%s -d \""$2"\""|getline d; if (last-d<=x)print $0 }' logFile 

我认为错误在于"date +%s -d ....这部分代码。出现了如下错误信息:
sh: -c: line 0: unexpected EOF while looking for matching `"'
sh: -c: line 1: syntax error: unexpected end of file
sh: -c: line 0: unexpected EOF while looking for matching `"'
sh: -c: line 1: syntax error: unexpected end of file

我花了很长时间在这里尝试解决问题,但没有找到任何解决方案。
该脚本将由crontab调用,以获取最近1分钟的日志行,并计算1分钟内一个IP地址在列表中出现的次数,以便我可以检测它是否是攻击。 这是另一个任务,希望专家能够在同一问题中提供所需的代码。 (我认为可以用2行代码解决)。

为什么要重复造轮子?已经有可以做到这样的工具了。 - Adrian Frühwirth
这些错误信息似乎表明您在某个地方漏掉了一个字符串的闭合双引号。我猜测它可能在第132行,第37个位置,在第三个if语句之前,但这完全是一个猜测,因为您没有发布实际的代码... - twalberg
代码已发布在链接中,并且我提到了最有用的答案在那个问题中。无论如何,我会编辑这个问题。这是澄清.sh文件、日志文件以及在终端中获得的结果的图片。http://postimg.org/image/lih0v0gzx/ - kingk110
2个回答

2
问题很可能只是您没有引用您的shell变量。看看这个例子:
$ foo='ab cd'

$ awk -v bar="$foo" 'BEGIN{print bar}'
ab cd

$ awk -v bar=$foo 'BEGIN{print bar}'
awk: fatal: cannot open file `BEGIN{print bar}' for reading (No such file or directory)

是的,我知道这是一个不同的错误信息——当你不加引号地使用shell变量时,会发生任何数量的事情,这取决于变量的值、目录的内容等等,其中一些非常糟糕,比如删除文件系统中的每个文件。

因此,请给您的变量加上引号:

-v last="$last" -v x="$x"

然后查看是否仍然存在问题。

顺便说一下,以下是如何使用GNU awk真正解决您的原始问题,并使用输入文件http://pastebin.com/BXmS4zLn

$ cat tst.awk
BEGIN {
    ARGV[ARGC++] = ARGV[ARGC-1]

    mths = "JanFebMarAprMayJunJulAugSepOctNovDec"

    if (days)  { hours = days * 24  }
    if (hours) { mins  = hours * 60 }
    if (mins)  { secs  = mins * 60  }
    deltaSecs = secs
}

NR==FNR {
    nr2secs[NR] = mktime($6" "(match(mths,$5)+2)/3" "$4" "gensub(/:/," ","g",$7))
    next
}

nr2secs[FNR] >= (nr2secs[NR-FNR] - deltaSecs)

$ awk -v hours=1 -f tst.awk file
157.55.34.99 - -  06 Sep 2013 09:13:10 +0300  "GET /index.php HTTP/1.1" 200 16977 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
85.163.134.149 - -  06 Sep 2013 09:50:23 +0300  "GET /wap/wapicons/mnrwap.jpg HTTP/1.1" 200 1217 "http://mydomain.com/main.php" "Mozilla/5.0 (Linux; U; Android 4.1.2; en-gb; GT-I9082 Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
83.113.48.218 - -  06 Sep 2013 10:13:07 +0300  "GET /english/nicons/word.gif HTTP/1.1" 200 803 "http://mydomain.com/french/details.php?eid=127928&cid=18&fromval=1&frid=18" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)"

$ gawk -v mins=60 -f tst.awk file
157.55.34.99 - -  06 Sep 2013 09:13:10 +0300  "GET /index.php HTTP/1.1" 200 16977 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
85.163.134.149 - -  06 Sep 2013 09:50:23 +0300  "GET /wap/wapicons/mnrwap.jpg HTTP/1.1" 200 1217 "http://mydomain.com/main.php" "Mozilla/5.0 (Linux; U; Android 4.1.2; en-gb; GT-I9082 Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
83.113.48.218 - -  06 Sep 2013 10:13:07 +0300  "GET /english/nicons/word.gif HTTP/1.1" 200 803 "http://mydomain.com/french/details.php?eid=127928&cid=18&fromval=1&frid=18" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)"

$ gawk -v mins=20 -f tst.awk file
83.113.48.218 - -  06 Sep 2013 10:13:07 +0300  "GET /english/nicons/word.gif HTTP/1.1" 200 803 "http://mydomain.com/french/details.php?eid=127928&cid=18&fromval=1&frid=18" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)"

您可以指定days=或hours=或mins=或secs=变量,它会自动处理。如果您只需要获取最近1分钟的日志行(现在?),并想要看到一个一行代码的解决方案:
$ gawk 'NR==FNR {nr2secs[++nr] = mktime($6" "(match("JanFebMarAprMayJunJulAugSepOctNovDec",$5)+2)/3" "$4" "gensub(/:/," ","g",$7)); next} nr2secs[FNR] >= (nr2secs[nr] - 60)' file file
83.113.48.218 - -  06 Sep 2013 10:13:07 +0300  "GET /english/nicons/word.gif HTTP/1.1" 200 803 "http://mydomain.com/french/details.php?eid=127928&cid=18&fromval=1&frid=18" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)"

1
根据您的输入这里,您可以使用以下脚本:
#!/bin/bash

LOGFILE=/path/to/logfile

X=$(( 60 * 60 )) ## 1 Hour

function get_ts {
    DATE="${1%%\]*}"; DATE="${DATE##*\[}"; DATE=${DATE/:/ }; DATE=${DATE//\// }
    TS=$(date -d "$DATE" '+%s')
}

get_ts "$(tail -n 1 "$LOGFILE")"
LAST=$TS

while read -r LINE; do
    get_ts "$LINE"
    (( (LAST - TS) <= X )) && echo "$LINE"
done < "$LOGFILE"

将其保存到文件中并更改LOGFILE的值,然后使用bash script.sh运行。

示例输出:

157.55.34.99 - - [06/Sep/2013:09:13:10 +0300] "GET /index.php HTTP/1.1" 200 16977 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
85.163.134.149 - - [06/Sep/2013:09:50:23 +0300] "GET /wap/wapicons/mnrwap.jpg HTTP/1.1" 200 1217 "http://mydomain.com/main.php" "Mozilla/5.0 (Linux; U; Android 4.1.2; en-gb; GT-I9082 Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"

显示相同的结果。我知道crontab的功能。我的意思是,将计算每个IP并给出跟随IP的调用URL数量的命令。 如果我搜索可以找到那个命令,这不是主要问题。 - kingk110
@kingk110 能否展示一下你使用的关键代码部分?特别是那个通过 sh -c 调用的部分。 - konsolebox
代码已发布在链接中,我提到最有用的答案就在那个问题中。无论如何,我会编辑这个问题。 - kingk110
@king110 这实际上是 awk 的限制。当你从它调用外部命令时,它依赖于 shell,将其传递以进行重新评估。如果你的输入包含可能改变语法的字符,它会导致语法错误。确保预期的命令字符串由 shell 语法正确,无论输入是否包含像 " 这样的危险字符。我知道这是你基于的内容,但我期望那不是你正在运行的代码,或者至少输入不同。请向我们展示它。 - konsolebox
这是我从日志文件中复制的不同时间样本: http://pastebin.com/BXmS4zLn - kingk110
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接