【问题标题】:Extract data from log file in specified range of time [duplicate]在指定时间范围内从日志文件中提取数据[重复]
【发布时间】:2011-11-26 09:04:30
【问题描述】:

我想使用基于时间范围的 shell 脚本 (bash) 从日志文件中提取信息。日志文件中的一行如下所示:

172.16.0.3 - - [31/Mar/2002:19:30:41 +0200] "GET / HTTP/1.1" 200 123 "" "Mozilla/5.0 (compatible; Konqueror/2.2.2-2; Linux)"

我想提取特定区间的数据。例如,我只需要查看最后记录的数据中最后 X 分钟或 X 天前发生的事件。我是 shell 脚本的新手,但我尝试过使用 grep 命令。

【问题讨论】:

  • 你熟悉awk/sed吗?
  • 这个问题需要缩小范围,这样它就不是那么通用了。如果问题是说“我想收集当前时间的所有日志”,那么可以回答这个问题。否则,这不是一个可以回答的问题,因为所有边缘情况,比如从 1 小时前收集日志(如果是上午 12:30 怎么办?),加上其他提出的问题,问题真的是问“哪些软件库可以帮助处理并解释标准日志文件?”。因为这个问题是这样解决的,而且不是简单的正则表达式。另外,昨天是闰日。 :-)

标签: bash


【解决方案1】:

您可以为此使用sed。例如:

$ sed -n '/Feb 23 13:55/,/Feb 23 14:00/p' /var/log/mail.log
Feb 23 13:55:01 messagerie postfix/smtpd[20964]: connect from localhost[127.0.0.1]
Feb 23 13:55:01 messagerie postfix/smtpd[20964]: lost connection after CONNECT from localhost[127.0.0.1]
Feb 23 13:55:01 messagerie postfix/smtpd[20964]: disconnect from localhost[127.0.0.1]
Feb 23 13:55:01 messagerie pop3d: Connection, ip=[::ffff:127.0.0.1]
...

工作原理

-n 开关告诉 sed 不要输出它读取的文件的每一行(默认行为)。

正则表达式后面的最后一个p 告诉它打印与前面的表达式匹配的行。

表达式'/pattern1/,/pattern2/' 将打印第一个模式和第二个模式之间的所有内容。在这种情况下,它将打印在字符串 Feb 23 13:55 和字符串 Feb 23 14:00 之间找到的每一行。

More info here

【讨论】:

  • 这似乎是最好的解决方案,缺乏解释来理解它对非 sed 上瘾者的作用,但这很棒。
  • 我的评论未被接受(太长),所以这里有一个 wiki page,关于该特定命令的工作原理、一般 sed 的工作原理以及为什么只知道 python 就应该学习 sed
  • NB 在满足 end 子句的第一行停止,因此如果有多个 14:00,则只返回第一个。
  • 对。您可以通过选择下一个时间戳作为地址范围的正确部分来轻松解决此问题。
  • 如果您知道日志中有哪些时间戳并且它是密集的,那么这是一种可行的方法。如果您没有每分钟/每小时/每天获得条目,则您的正则表达式可能在您的开始或结束正则表达式上没有匹配项,那么您将一无所获,或者分别获得太多。
【解决方案2】:

使用 grep 和正则表达式,例如,如果您想要 4 分钟的日志间隔:

grep "31/Mar/2002:19:3[1-5]" logfile

将返回 2002 年 3 月 31 日 19:31 到 19:35 之间的所有日志行。 假设您需要从 2011 年 9 月 27 日开始的最后 5 天,您可以使用以下内容:

grep "2[3-7]/Sep/2011" logfile

【讨论】:

  • 我的意见:grep 不是解决这个问题的正确工具。使用正则表达式比较日期可能非常困难。例如,我想要上一条记录 3 天 15 小时 32 分钟前的所有记录。它也可能有月份变化(例如一个月的最后/第一天),日期变化(一天的第一/最后一个小时),甚至年份变化。即使有可能,使用正则表达式也可能非常复杂。
  • 您可能是对的,但我的解决方案是一个复杂问题的快速而肮脏的解决方案,尤其是对于这种日期格式。如果你想有很多电源过滤日志,你可能应该使用不同的工具......
【解决方案3】:

好吧,我花了一些时间研究你的日期格式.....

但是,我终于解决了..

让我们举一个示例文件(名为logFile),我把它写得有点短。 比如说,你想在这个文件中获取最后 5 分钟的日志:

172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:20:41 +0200] "GET 
### lines below are what you want (5 mins till the last record)
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:27:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:30:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:30:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:30:41 +0200] "GET 
172.16.0.3 - - [31/Mar/2002:19:30:41 +0200] "GET 

解决办法如下:

# 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      

输出:

172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:27:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:30:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:30:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:30:41 +0200  "GET 
172.16.0.3 - -  31 Mar 2002 19:30:41 +0200  "GET

编辑

您可能会注意到输出中的 [ 和 ] 消失了。如果您确实想要他们回来,您可以更改最后一行 awk print $0 -> print $1 "[" $2 "]" $3

【讨论】:

  • 我实际上并不了解解决方案,因为我是 sed 和 awk 的新手。我的日志文件包含如下行 2014-02-27 21:37:35 supervisor [INFO] Starting supervisor with id 3100de93-8c33-43a9-8e2f-2b8c3d926831 at host 我如何从这个日志文件中提取最后发生的行分钟?请帮忙
【解决方案4】:

我使用此命令查找特定事件“DHCPACK”的最后 5 分钟日志,请尝试以下操作:

$ grep "DHCPACK" /var/log/messages | grep "$(date +%h\ %d) [$(date --date='5 min ago' %H)-$(date +%H)]:*:*"

【讨论】:

    【解决方案5】:

    您可以使用它来获取当前和日志时间:

    #!/bin/bash
    
    log="log_file_name"
    while read line
    do
      current_hours=`date | awk 'BEGIN{FS="[ :]+"}; {print $4}'`
      current_minutes=`date | awk 'BEGIN{FS="[ :]+"}; {print $5}'`
      current_seconds=`date | awk 'BEGIN{FS="[ :]+"}; {print $6}'`
    
      log_file_hours=`echo $line | awk 'BEGIN{FS="[ [/:]+"}; {print  $7}'`
      log_file_minutes=`echo $line | awk 'BEGIN{FS="[ [/:]+"}; {print  $8}'`
      log_file_seconds=`echo $line | awk 'BEGIN{FS="[ [/:]+"}; {print  $9}'`    
    done < $log
    

    并比较 log_file_*current_* 变量。

    【讨论】:

    • 这段代码有多个问题。您应该使用 read -r 并在插值时正确引用 $line$log 的值。大量外部进程浪费在可以用 Bash 内部完成的东西上,更简单地说,这也反对这种解决方案,尽管它在技术上当然不是错误的。
    猜你喜欢
    • 2013-09-10
    • 1970-01-01
    • 2016-12-16
    • 2018-06-22
    • 2021-01-29
    • 1970-01-01
    • 2015-05-03
    • 2013-02-15
    • 1970-01-01
    相关资源
    最近更新 更多