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

Posted

技术标签:

【中文标题】使用 awk getline bash 在指定的时间范围内从日志文件中提取数据【英文标题】:extract data from log file in specified range of time with awk getline bash 【发布时间】:2013-09-10 22:14:06 【问题描述】:

我正在搜索解析日志文件并在此链接中找到我需要的内容 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 分钟的日志行,并计算一个 ip 在一分钟内列出了多少次,这样我就可以检测它是否是攻击。这是另一项任务,希望专家能帮助在同一问题中提供所需的代码。(我认为它可以用 2 行解决)。

【问题讨论】:

为什么要重新发明***?已经有一些工具可以做这样的事情。 这些错误消息似乎表明您在某处的字符串上遗漏了右双引号。我猜它可能在第 132 行,第 37 位,就在第三个 if 语句之前,但这完全是猜测,因为您还没有发布实际代码.... 代码发布在链接中,我提到最有用的答案就是那个问题。无论如何我都会编辑这个问题。这是阐明 .sh 文件、日志文件和在终端 postimg.org/image/lih0v0gzx 中获得的结果的图像 【参考方案1】:

问题可能只是你没有引用你的 shell 变量。看:

$ foo='ab cd'

$ awk -v bar="$foo" 'BEGINprint bar'
ab cd

$ awk -v bar=$foo 'BEGINprint bar'
awk: fatal: cannot open file `BEGINprint 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)"

【讨论】:

【参考方案2】:

根据您的输入 here,您可以使用如下脚本:

#!/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 并给出调用 url 的数量,然后是 IP。如果我搜索它不是主要问题,我可能会发现该命令。 @kingk110 介意向我们展示您使用的代码的一些关键部分吗?尤其是通过sh -c调用的那个。 代码发布在链接中,我提到最有用的答案就是那个问题。无论如何我都会编辑这个问题。 @king110 这实际上是 awk 的局限性。当您从中调用外部命令时,它取决于外壳程序,并将其传递给重新评估。如果您的输入以某种方式包含可能会改变语法的字符,则会导致语法错误。确保将由 shell 评估的预期命令字符串在语法上是正确的,无论输入是否为您提供像 " 这样的危险字符。我也知道这是您的基础,但我希望这不完全是您正在运行的代码,或者至少输入不一样。请出示给我们。 这里是 .sh 文件和 logFile 以及执行结果postimg.org/image/lih0v0gzx

以上是关于使用 awk getline bash 在指定的时间范围内从日志文件中提取数据的主要内容,如果未能解决你的问题,请参考以下文章

awk next以及getline用法示例

awk中next和getline的区别

将前导零添加到 awk 变量

shell脚本之删除内容相同的重复文件

如何在 getline 管道中获取命令的退出状态?

shell awk如何输出文件的指定列