Bash 从日志文件中提取最后10分钟

Bash 从日志文件中提取最后10分钟,bash,date,datetime,logfiles,perl,Bash,Date,Datetime,Logfiles,Perl,为了找到一种观看最近事件的简单方法(不到10分钟),我尝试了以下方法: awk "/^$(date --date="-10 min" "+%b %_d %H:%M")/{p++} p" /root/test.txt 但它并没有像预期的那样工作 日志文件的格式为: Dec 18 09:48:54 Blah Dec 18 09:54:47 blah bla Dec 18 09:55:33 sds Dec 18 09:55:38 sds Dec 18 09:57:58 sa Dec 18 09:58

为了找到一种观看最近事件的简单方法(不到10分钟),我尝试了以下方法:

awk "/^$(date --date="-10 min" "+%b %_d %H:%M")/{p++} p" /root/test.txt
但它并没有像预期的那样工作

日志文件的格式为:

Dec 18 09:48:54 Blah
Dec 18 09:54:47 blah bla
Dec 18 09:55:33 sds
Dec 18 09:55:38 sds
Dec 18 09:57:58 sa
Dec 18 09:58:10 And so on...
在中,可以使用
date
命令解析时间戳。这个这个简单的整数很容易就能精确地完成基本的算术运算

如果需要实际时间最后10分钟的日志消息:

now10=$(($(date +%s) - (10 * 60)))

while read line; do
    [ $(date -d "${line:0:15}" +%s) -gt $now10 ] && printf "$line\n"
done < logfile
以下是对上述性能的轻微增强:

$ { while read line; do
> [ $(date -d "${line:0:15}" +%s) -gt $last10 ] && printf "$line\n" && break
> done ; cat ; }  < logfile
Dec 18 10:19:16
Dec 18 10:19:23
Dec 18 10:21:03
Dec 18 10:22:54
Dec 18 10:27:32
$ 
${读取行时;执行
>[$(日期-d“${line:0:15}”+%s)-gt$last10]&&printf“$line\n”&&break
>完成;cat;}<日志文件
12月18日10:19:16
12月18日10:19:23
12月18日10:21:03
12月18日10:22:54
12月18日10:27:32
$ 

这假设日志条目严格按时间顺序排列。一旦我们匹配了所讨论的时间戳,我们就退出for循环,然后只使用
cat
转储其余的条目。

在python中,您可以执行以下操作:

from datetime import datetime

astack=[]
with open("x.txt") as f:
    for aline in f:
        astack.append(aline.strip())
lasttime=datetime.strptime(astack[-1], '%b %d %I:%M:%S')
for i in astack:
    if (lasttime - datetime.strptime(i, '%b %d %I:%M:%S')).seconds <= 600:
        print i

您可以使用简单的字符串比较来匹配日期范围,例如:

d1=$(date --date="-10 min" "+%b %_d %H:%M")
d2=$(date "+%b %_d %H:%M")
while read line; do
    [[ $line > $d1 && $line < $d2 || $line =~ $d2 ]] && echo $line
done
或使用
awk
,如果您愿意:

awk -v d1="$d1" -v d2="$d2" '$0 > d1 && $0 < d2 || $0 ~ d2'
awk-vd1=“$d1”-vd2=“$d2””$0>d1&&0
简介 这个答案很长,因为有三种不同的思考方式:1)快速或准确,2)纯和3)函数脚本

这是一个(普通的)工作!: 简单高效:

perl -MDate::Parse -ne 'print if/^(.{15})\s/&&str2time($1)>time-600' /path/log
此版本使用
时间
功能打印最后10分钟事件,直到现在

您可以使用以下方法对此进行测试:

sudo cat /var/log/syslog |
  perl -MDate::Parse -ne '
    print if /^(\S+\s+\d+\s+\d+:\d+:\d+)\s/ && str2time($1) > time-600'
请注意,第一个表示只使用每行的前15个字符,而第二个构造使用更详细的regexp

作为perl脚本:
last10m.pl

#!/usr/bin/perl -wn

use strict;
use Date::Parse;
print if /^(\S+\s+\d+\s+\d+:\d+:\d+)\s/ && str2time($1) > time-600
严格地说:从日志文件中提取最后10分钟 这意味着不是相对于当前时间,而是相对于日志文件中的最后一个条目:

检索期末有两种方法:

其中,逻辑上,日志文件的最后修改时间必须是最后一个条目的时间

因此,该命令可以变成:

perl -MDate::Parse -ne 'print if/^(.{15})\s/&&str2time($1)>'$(
    date -r logfile +%s)
或者,您可以将最后一个条目作为参考:

perl -MDate::Parse -E 'open IN,"<".$ARGV[0];seek IN,-200,2;while (<IN>) {
    $ref=str2time($1) if /^(\S+\s+\d+\s+\d+:\d+:\d+)/;};seek IN,0,0;
    while (<IN>) {print if /^(.{15})\s/&&str2time($1)>$ref-600}' logfile
存储此脚本并运行:

cat >last10min.sh
chmod +x last10min.sh
sudo cat /var/log/syslog | ./last10min.sh
严格地说:从日志文件中提取最后10分钟 只需替换第10行,但必须将文件名放在脚本中,而不要将其用作筛选器:

#!/bin/bash

declare -A month

for i in {1..12};do
    LANG=C printf -v var "%(%b)T" $(((i-1)*31*86400))
    month[$var]=$i
  done

read now < <(date -d "$(tail -n1 $1|head -c 15)" +%s)
printf -v ref "%(%m%d%H%M%S)T" $((now-600))

export -A month

{
    while read line;do
        printf -v crt "%02d%02d%02d%02d%02d" ${month[${line:0:3}]} \
            $((10#${line:4:2})) $((10#${line:7:2})) $((10#${line:10:2})) \
            $((10#${line:13:2}))
        [ $crt -gt $ref ] && break
    done
    cat
} <$1
用法:

recentLog[文件名][分钟][时间长度]

  • 日志文件的文件名
  • minutes
    max before now要显示的行数
  • 从行开始的时间长度
    (默认值
    15
    • Ruby解决方案(在Ruby 1.9.3上测试) 您可以将天、小时、分钟或秒作为参数传递,它将在指定的文件(或目录,在这种情况下,它将在名称后面附加“/*”)上搜索表达式和:

      在您的情况下,只需像这样调用脚本:$0-m10“expression”log\u文件

      注意:如果您知道“ruby”的位置,请更改shebang(脚本的第一行), 出于安全原因

      #! /usr/bin/env ruby
      
      require 'date'
      require 'pathname'
      
      if ARGV.length != 4
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
              exit 1
      end
      begin
              total_amount = Integer ARGV[1]
      rescue ArgumentError
              $stderr.print "error: parameter 'time' must be an Integer\n"
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
      end
      
      if ARGV[0] == "-m"
              gap = Rational(60, 86400)
              time_str = "%b %d %H:%M"
      elsif ARGV[0] == "-s"
              gap = Rational(1, 86400)
              time_str = "%b %d %H:%M:%S"
      elsif ARGV[0] == "-h"
              gap = Rational(3600, 86400)
              time_str = "%b %d %H"
      elsif ARGV[0] == "-d"
              time_str = "%b %d"
              gap = 1
      else
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
              exit 1
      end
      
      pn = Pathname.new(ARGV[3])
      if pn.exist?
              log = (pn.directory?) ? ARGV[3] + "/*" : ARGV[3]
      else
              $stderr.print "error: file '" << ARGV[3] << "' does not exist\n"
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
      end
      
      search_str = ARGV[2]
      now = DateTime.now
      
      total_amount.times do
              now -= gap
              system "cat " << log << " | grep '" << now.strftime(time_str) << ".*" << search_str << "'"
      end
      
      #/usr/bin/env ruby
      需要“日期”
      需要“路径名”
      如果ARGV.length!=4.
      $stderr.print“用法:#{$0}-d |-h |-m |-s时间表达式日志文件\n”
      出口1
      结束
      开始
      总金额=整数ARGV[1]
      挽救错误
      $stderr.print“错误:参数'time'必须是整数\n”
      $stderr.print“用法:#{$0}-d |-h |-m |-s时间表达式日志文件\n”
      结束
      如果ARGV[0]=“-m”
      差距=合理(6086400)
      time_str=“%b%d%H:%M”
      elsif ARGV[0]=“s”
      差距=合理(186400)
      time_str=“%b%d%H:%M:%S”
      elsif ARGV[0]=“-h”
      差距=合理(360086400)
      time_str=“%b%d%H”
      elsif ARGV[0]=“d”
      time_str=“%b%d”
      差距=1
      其他的
      $stderr.print“用法:#{$0}-d |-h |-m |-s时间表达式日志文件\n”
      出口1
      结束
      pn=路径名。新建(ARGV[3])
      是否存在pn?
      日志=(pn.目录?)?ARGV[3]+“/*”:ARGV[3]
      其他的
      $stderr.print“error:file'”这是一个很好的工具
      从-10到现在,范围是您想要的任何范围

      sed -n "/^$(date --date='10 minutes ago' '+%b %_d %H:%M')/,\$p" /var/log/blaaaa
      

      您想要实际时间的最后10分钟的日志消息,还是相对于日志末尾的最后10分钟的日志消息?这种类型的内容在普通shell中变得很混乱——特别是因为您必须为解决方案使用的日期命令因系统而异。我可以用Perl给你一个答案吗?如果没有,我会给你一个适用于BSD的(我有一个Mac),你必须为Linux找到它(如果你有)。bash现在提供了很多强大的技巧来实现这类技巧。。。(见我的答案)。无论如何,perl仍然是这类工作的首选。@tripleee这不是真正的重复,因为目标是在文件结束前读取大量时间。另一个问题代表一个日志文件中的时间间隔。@FHauri没有结束条件只是同一问题的一个简单例子,不是吗?如果时间戳中有不同的月份呢?我认为字符串比较可能会失败。e、 g.
      [“Jan 18 10:27:32”<“Feb 18 10:27:32”]&&echo“older”
      [“Feb 18 10:27:32”<“Mar 18 10:27:32”]&&echo“older”
      给出了不一致的结果。@DigitalTrampion True,如果时间段涵盖了月份的变化,我的解决方案将不起作用。您的解决方案更准确,我向您投了赞成票。@janos新的bash版本通过使用
      printf“%(…)T”-1
      提供了一个内置的
      date
      函数。这比使用叉子记录日期要快得多。要使用日期的endianess属性,可以使用关联数组更正月份条目。看看我的答案@好的,我投你一票;-)我想知道为什么它会返回以下内容:./test.sh:第8行:[:-gt:一元运算符预期日期:无效日期
      [pid 26481 on./test.sh:第8行:[:-gt:一元运算符预期日期:无效日期
      [pid 17563 on./test.sh:第8行:[:-gt:一元运算符预期12月18日10:19:16 Dec]
      
      #!/usr/bin/perl -w
      
      use strict;
      use Date::Parse;
      my $ref;                 # The only variable I will use in this.
      
      open IN,"<".$ARGV[0];    # Open (READ) file submited as 1st argument
      seek IN,-200,2;          # Jump to 200 character before end of logfile. (This
                               # could not suffice if log file hold very log lines! )
      while (<IN>) {           # Until end of logfile...
          $ref=str2time($1) if /^(\S+\s+\d+\s+\d+:\d+:\d+)/;
      };                       # store time into $ref variable.
      seek IN,0,0;             # Jump back to the begin of file
      while (<IN>) {
          print if /^(.{15})\s/&&str2time($1)>$ref-600;
      }
      
      #!/bin/bash
      
      declare -A month
      
      for i in {1..12};do
          LANG=C printf -v var "%(%b)T" $(((i-1)*31*86400))
          month[$var]=$i
        done
      
      printf -v now "%(%s)T" -1
      printf -v ref "%(%m%d%H%M%S)T" $((now-600))
      
      while read line;do
          printf -v crt "%02d%02d%02d%02d%02d" ${month[${line:0:3}]} \
              $((10#${line:4:2})) $((10#${line:7:2})) $((10#${line:10:2})) \
              $((10#${line:13:2}))
          # echo " $crt < $ref ??"   # Uncomment this line to print each test
          [ $crt -gt $ref ] && break
      done
      cat
      
      cat >last10min.sh
      chmod +x last10min.sh
      sudo cat /var/log/syslog | ./last10min.sh
      
      #!/bin/bash
      
      declare -A month
      
      for i in {1..12};do
          LANG=C printf -v var "%(%b)T" $(((i-1)*31*86400))
          month[$var]=$i
        done
      
      read now < <(date -d "$(tail -n1 $1|head -c 15)" +%s)
      printf -v ref "%(%m%d%H%M%S)T" $((now-600))
      
      export -A month
      
      {
          while read line;do
              printf -v crt "%02d%02d%02d%02d%02d" ${month[${line:0:3}]} \
                  $((10#${line:4:2})) $((10#${line:7:2})) $((10#${line:10:2})) \
                  $((10#${line:13:2}))
              [ $crt -gt $ref ] && break
          done
          cat
      } <$1
      
      recentLog(){ 
          perl -MDate::Parse -ne '
              print if/^(.{'${3:-15}'})\s/ &&
                  str2time($1)>time-'$((
                      60*${2:-10}
                  )) ${1:-/var/log/daemon.log}
      }
      
      #! /usr/bin/env ruby
      
      require 'date'
      require 'pathname'
      
      if ARGV.length != 4
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
              exit 1
      end
      begin
              total_amount = Integer ARGV[1]
      rescue ArgumentError
              $stderr.print "error: parameter 'time' must be an Integer\n"
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
      end
      
      if ARGV[0] == "-m"
              gap = Rational(60, 86400)
              time_str = "%b %d %H:%M"
      elsif ARGV[0] == "-s"
              gap = Rational(1, 86400)
              time_str = "%b %d %H:%M:%S"
      elsif ARGV[0] == "-h"
              gap = Rational(3600, 86400)
              time_str = "%b %d %H"
      elsif ARGV[0] == "-d"
              time_str = "%b %d"
              gap = 1
      else
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
              exit 1
      end
      
      pn = Pathname.new(ARGV[3])
      if pn.exist?
              log = (pn.directory?) ? ARGV[3] + "/*" : ARGV[3]
      else
              $stderr.print "error: file '" << ARGV[3] << "' does not exist\n"
              $stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
      end
      
      search_str = ARGV[2]
      now = DateTime.now
      
      total_amount.times do
              now -= gap
              system "cat " << log << " | grep '" << now.strftime(time_str) << ".*" << search_str << "'"
      end
      
      sed -n "/^$(date --date='10 minutes ago' '+%b %_d %H:%M')/,\$p" /var/log/blaaaa