试图找到一种简单的方法来观察最近的事件(不到 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...
这是一个不错的工具 范围是您想要从 -10 到现在的任何
范围sed -n "/^$(date --date='10 minutes ago' '+%b %_d %H:%M')/,$p" /var/log/blaaaa
简介
这个答案很长,因为有3种不同的思维方式:1)perl快速或准确,2)纯bash和3)bash函数中的perl脚本。
这是 perl 的(常见)工作!
简单高效:
perl -MDate::Parse -ne 'print if/^(.{15})s/&&str2time($1)>time-600' /path/log
此版本通过使用time
功能打印最近 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 个字符,而第二个构造使用更详细的正则表达式。
作为 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 分钟
含义不是相对于当前时间,而是相对于日志文件中的最后一个条目:
有两种方法可以检索期末:
date -r logfile +%s
tail -n1 logfile | perl -MDate::Parse -nE 'say str2time($1) if /^(.{15})/'
从逻辑上讲,日志文件的上次修改时间必须是最后一个条目的时间。
因此,该命令可以变为:
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
第二个版本似乎更强,但只能访问一次文件。
作为一个perl脚本,这可能看起来像:
#!/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;
}
但如果你真的想使用 bash
有一个非常快速的纯 bash脚本:
警告:这使用最近的bashisms,需要$BASH_VERSION
4.2 或更高版本。
#!/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
严格:从日志文件中提取最后 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
将 perl 脚本转换为 bash 函数
正如ajcg
所评论的那样,将高效的perl脚本放入bash函数中可能会很好:
recentLog(){
perl -MDate::Parse -ne '
print if/^(.{'${3:-15}'})s/ &&
str2time($1)>time-'$((
60*${2:-10}
)) ${1:-/var/log/daemon.log}
}
用法:
recentLog [filename] [minutes] [time sting length]
- 日志文件
filename
minutes
现在之前要显示的行数最大值- 从行首
time sting length
(默认15
)。
您可以使用简单的字符串比较来匹配日期范围,例如:
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
例如,如果d1='Dec 18 10:19'
和d2='Dec 18 10:27'
则输出将为:
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
或者如果您愿意,可以使用awk
:
awk -v d1="$d1" -v d2="$d2" '$0 > d1 && $0 < d2 || $0 ~ d2'
在 bash 中,您可以使用date
命令来解析时间戳。 "%s"格式说明符将给定日期转换为自 1970-01-01 00:00:00 UTC 以来的秒数。 这个简单的整数很容易和准确地进行基本算术。
如果要从实际时间的最后 10 分钟记录消息:
now10=$(($(date +%s) - (10 * 60)))
while read line; do
[ $(date -d "${line:0:15}" +%s) -gt $now10 ] && printf "$linen"
done < logfile
请注意,${line:0:15}
表达式是一个 bash 参数扩展,它给出了行的前 15 个字符,即时间戳本身。
如果要显示过去 10 分钟相对于日志末尾的日志消息:
$ lastline=$(tail -n1 logfile)
$ last10=$(($(date -d "$lastline" +%s) - (10 * 60)))
$ while read line; do
> [ $(date -d "${line:0:15}" +%s) -gt $last10 ] && printf "$linen"
> done < 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
$
以下是上述性能的温和增强:
$ { while read line; do
> [ $(date -d "${line:0:15}" +%s) -gt $last10 ] && printf "$linen" && 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
$
这假定日志条目严格按时间顺序排列。 一旦我们匹配了有问题的时间戳,我们就会退出 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
将文件中的行放入堆栈(python列表)中。 弹出最后一项并获取连续日期项目之间的差异,直到获得小于 600 秒的差异。
在您的输入上运行,我得到以下内容:
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
Ruby 解决方案(在 Ruby 1.9.3 上测试)
您可以将天、小时、分钟或秒作为参数传递,它将搜索表达式并在指定的文件(或目录,在这种情况下,它将在名称后附加"/*"):
在您的情况下,只需像这样调用脚本:$0 -m 10"表达式"log_file
注意:另外,如果您知道"红宝石"的位置,请更改shebang(脚本的第一行), 出于安全原因。
#! /usr/bin/env ruby
require 'date'
require 'pathname'
if ARGV.length != 4
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_filen"
exit 1
end
begin
total_amount = Integer ARGV[1]
rescue ArgumentError
$stderr.print "error: parameter 'time' must be an Integern"
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_filen"
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_filen"
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 existn"
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_filen"
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