如何使用Bash在文件中搜索两个时间戳之间的行

11
在bash中,我正在尝试读取日志文件,并仅打印那些时间戳在两个特定时间之间的行。时间格式为hh:mm:ss。例如,我将搜索落在12:52:33到12:59:33之间的行。
我想使用正则表达式,因为我可以在grep函数中使用它。
每个日志行都以some_nr 2014-05-15 21:58:00,000000 rest_of_line开始。
我的解决方案给我了1分钟的余地。 我去掉了ss并使用hh:mm:[0-9]{2}获取所有行。 $2的格式为filename_hh:mm:;例如: "24249_16:05:;24249_16:05:;24249_16:07:;24249_16:07:;24249_16:08:"
我的代码:
B=$2  

for line in ${B//;/ } ;
do  
    TENT=`echo $line | awk '{split($0,numbers,"_"); print numbers[1]}'`"_logs.txt"
    TIME=`echo $line | awk '{split($0,numbers,"_"); print numbers[2]}'`"[0-9]{2}"

    grep -iE ${TIME} ${TENT} >> ${FILE1}
done

我需要一个在任何时间不是60秒的情况下有15秒余地的解决方案。我希望输入格式为 filename_hh:mm:ss,并获取 hh:mm:ss +/- 15s 或 filename_hh:mm:ss(1)_hh:mm:ss(2) 之间的行。有时候没有行,因此解决方案应该“识别”是否匹配所输入的时间间隔。

日志文件看起来像这样:

1002143 1002143 2014/15/05 22:09:52.937004 bla 
1002130         2014/15/05 22:09:44.786002 bla bla
1001667         2014/15/05 22:09:44.592009 bl a bla
1001667 1001667 2014/15/05 22:09:44.592009 bl a bla

你能否发布一份日志文件的样本? - anubhava
是的,我已经在上面的问题中添加了翻译。 - herder
5个回答

18

我认为sed是最好的选择:

sed -rne '/<timestamp>/,/<timestamp>/ p' <file>

示例:

tiago@dell:~$ sed -rne '/08:17:38/,/08:24:36/ p' /var/log/syslog 
May 16 08:17:38 dell AptDaemon.Worker: INFO: 处理事务 /org/debian/apt/transaction/08a244f7b8ce4fad9f6b304aca9eae7a
May 16 08:17:50 dell AptDaemon.Worker: INFO: 完成事务 /org/debian/apt/transaction/08a244f7b8ce4fad9f6b304aca9eae7a
May 16 08:18:50 dell AptDaemon.PackageKit: INFO: 初始化 PackageKit 事务
May 16 08:18:50 dell AptDaemon.Worker: INFO: 模拟 trans: /org/debian/apt/transaction/37c3ef54a6ba4933a561c49b3fac5f6e
May 16 08:18:50 dell AptDaemon.Worker: INFO: 处理事务 /org/debian/apt/transaction/37c3ef54a6ba4933a561c49b3fac5f6e
May 16 08:18:51 dell AptDaemon.PackageKit: INFO: 获取更新()
May 16 08:18:52 dell AptDaemon.Worker: INFO: 完成事务 /org/debian/apt/transaction/37c3ef54a6ba4933a561c49b3fac5f6e
May 16 08:24:36 dell AptDaemon: INFO: 因长时间不活动而退出

不,sed绝对不是“最佳”选项。考虑一下日志输出可能具有时间戳。 - Kent
1
@kent 在这种情况下,可以匹配行的开头。 - Tiago Lopo
我的日志行以“2014-05-15 21:58:00,000000”开头,由于这些毫秒数,我想使用grep。 - herder
您介意分享一下您正在使用的grep命令吗? - Tiago Lopo
我将我的代码添加在上面的问题中。 - herder
显示剩余4条评论

10

日志文件通常按时间戳排序,假设时间戳在第一列,您可以:

awk -v from="12:52:33" -v to="12:59:33" '$1>=from && $1<=to' foo.log

通过这种方式,您可以更改from和to以获取不同的日志条目集。正则表达式不是进行数字计算/比较的好工具。


这个解决方案也不起作用。在我的日志文件中,第二列有时是一个数字,有时不是 - 上面的代码不能正确识别列。如果第二列是一个数字,则将时间看作第四列,如果不是,则看作第三列。 - herder
@herder,这个解决方案是针对你第一个问题版本的。你编辑了它并添加了新内容,所以这个解决方案将会失败。但是awk有日期相关的函数,也可以调用外部命令,它可以完成你的工作。阅读一些教程吧。 - Kent
我感谢你的帮助 - 我知道这是针对第一个问题版本的,但我没有预料到那些问题。我会在 awk 手册中查找 ;) - herder
awk -v from=$TIME1".000" -v to=$TIME2".000" '{for (i=3; i<=4; i++) if ($i~"([0-9]{2}:){2}[0-9]{2}.[0-9]{3}" && ($i<=to && $i>=from)) print $0}' $TENT将上述程序相关内容从英语翻译成中文。仅返回已翻译的文本。 - herder
我使用if语句来检查哪一列是时间格式。但实际上使用Perl更加方便。感谢大家的帮助。 - herder

4
您可以在egrep中使用这个正则表达式:
egrep '12:5[2-9]:33' file.log

我需要一般解决方案,不仅仅针对这个例子,而是适用于任何两次。 - herder
当然,这也是可能的,但是1.最好在你的问题中澄清这一点,2.展示你自己的尝试。 - anubhava
我在上面的问题中加入了我的尝试。 - herder

2
您正在使用错误的工具来完成此任务。一旦您有了像@anubhava提供的正则表达式,您就可以轻松找到不匹配它的时间间隔。grep和正则表达式可能适用于一些特殊情况,但它们无法扩展到一般情况。
您可以使用一些实际“理解”时间戳的工具吗?可能每种脚本语言(perl、python、ruby、lua)都有内置或库支持解析时间和日期。
但是,您可能可以利用GNU日期的功能:
% date --date="2014-05-15 21:58:00 15 sec ago" +'%Y-%m-%d %H:%M:%S'
2014-05-15 21:57:45
% date --date="2014-05-15 21:58:00 15 sec" +'%Y-%m-%d %H:%M:%S' 
2014-05-15 21:58:15

将其插入Tiago的sed过滤器想法中。

你说得没错,但这不是一个答案,伙计。 - Kent
抱歉,我忍不住了... - Stefan Schmiedl

1
您可以尝试以下Perl脚本:

#! /usr/bin/perl

use warnings;
use strict;
use Time::Piece;
use autodie;

my $arg=shift;
my @a =split("_",$arg);
my $fn=shift @a;

my $dfmt='%Y/%d/%m';
my $fmt=$dfmt.' %H:%M:%S';
my $t = localtime;
my $date=$t->strftime($dfmt);
my $t1; my $t2;
if (@a == 1) {
   my $d=$date.' '.$a[0];
   my $tt=Time::Piece->strptime($d, $fmt);
   $t1=$tt-15;
   $t2=$tt+15;
} elsif (@a == 2) {
   $t1=Time::Piece->strptime($date.' '.$a[0], $fmt);
   $t2=Time::Piece->strptime($date.' '.$a[1], $fmt);
} else {
   die "Unexpected input argument!";
}

$fn=$fn.'_logs.txt';
doGrep($fn,$t1,$t2,$fmt);

sub doGrep { 
   my ($fn,$t1,$t2,$fmt) = @_;

   open (my $fh, "<", $fn);
   while (my $line=<$fh>) {
      my ($d1,$d2) = $line=~/\S+\s+(\S+)\s+(\d\d:\d\d:\d\d)/;
      my $d=$d1.' '.$d2;
      my $t=Time::Piece->strptime($d, $fmt);
      print $line if ($t>$t1 && $t<$t2);
   }
   close ($fh);
}

使用以下语法从命令行运行它:./p.pl A_22:09:14


我遇到了一个错误:"在C:/Perl64/lib/Time/Piece.pm的第469行解析时间时出错,<$_[...]>的第1行。" - herder
@herder 也许您可以在 http://pastebin.com/ 上粘贴一份日志文件的示例呢?那么我也可以在我的机器上运行它,以进行检查。 - Håkon Hægland
1
@herder 谢谢。看起来你的文件日期格式与你在问题中提供的不同。比较一下 04/16/142014/15/05 - Håkon Hægland
1
@herder 在你的问题中,你只指定了一个时间间隔。没有关于日期的要求。因此,脚本假定它是当前日期。然而,看起来日志文件不是来自当前日期。你想怎么做?也在命令行上指定一个日期吗? - Håkon Hægland
@HH 抱歉,我想忽略它。 - herder
显示剩余3条评论

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