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

时间:2010-06-28 18:00:01

标签: regex perl timestamp

在Perl中我试图读取一个日志文件,并且只打印具有两个特定时间之间的时间戳的行。时间格式为hh:mm:ss,这始终是每个日志的第三个值。例如,我会搜索介于12:52:33到12:59:33之间的行

我是Perl的新手,并且不知道采取哪条路线来开始编程。我很确定这会使用某种类型的正则表达式,但对于我的生活,我甚至无法理解那将是什么。有人可以帮助我。

另外,为了使这更加困难,我必须使用核心Perl模块,因为我的公司不允许我使用任何其他模块,直到它们经过测试和验证,对任何系统都没有任何不良影响脚本可以与之交互。

4 个答案:

答案 0 :(得分:2)

在伪代码中,你会做这样的事情:

  • 逐行读取文件:
    • 解析此行的时间戳。
    • 如果小于开始时间,请跳至下一行。
    • 如果它大于结束时间,请跳到下一行!
    • else:这是你想要的一行:打印出来。

根据您的需要,这可能太高级了,但flip-flop operator ..会立即浮现在脑海中。

对于从stdin读取文件,这是传统模式:

while (my $line = <>)
{
     # do stuff...
}

使用split可以轻松地将一行划分为字段(请参阅perldoc -f split)。您可能需要按标签或空格分割线条,具体取决于格式。

一旦获得特定字段(包含时间戳),就可以使用自定义的正则表达式来检查它。请阅读perldoc perlre上的内容。

这可能会让你更接近:

use strict;
use warnings;

use POSIX 'mktime';
my $starttime = mktime(33, 52, 12);
my $endtime = mktime(33, 59, 12);

while (my $line = <>)
{
    # split into fields using whitespace as the delimiter
    my @fields = split(/\s+/, $line);

    # the timestamp is the 3rd field
    my $timestamp = $fields[2];

    my ($hour, $min, $sec) = split(':', $timestamp);
    my $time = mktime($sec, $min, $hour);

    next unless ($time < $starttime) .. ($time > $endtime);
    print $line;
}

答案 1 :(得分:2)

如果知道开始和结束时间,那么你需要一个带触发器操作符的Perl单线程:

perl -ne 'print if /12:52:33/../12:59:33/' logFile

如果您需要一些基础逻辑来确定开始和结束时间,那么将一行内容“展开”为正式脚本:

use strict;
use warnings;

open my $log, '<', 'logFile';

my $startTime = get_start_time();  # Sets $startTime in hh:mm:ss format
my $endTime = get_end_time();      # Sets $endTime in hh:mm:ss format

while ( <$log> ) {

    print if /$startTime/../$endTime/;
}

正如以太的评论所指出的,如果没有确切的时间,这将失败。如果有可能,可以改为实现以下逻辑:

use strict;
use warnings;
use autosplit;

open my $log, '<', 'logFile';

my $startTime = get_start_time();  # Sets $startTime in hh:mm:ss format
my $endTime = get_end_time();      # Sets $endTime in hh:mm:ss format

while ( <$log> ) {

    my $time = (split /,/, $_)[2];      # Assuming fields are comma-separated
                                        # and timelog is 3rd field

    last  if $time gt $endTime;         # Stop when stop time reached
    print if $time ge $startTime;
}

答案 2 :(得分:1)

如果文件中的每一行都有时间戳,那么在'sed'中你可以写:

sed -n '/12:52:33/,/12:59:33/p' logfile

这将回应相关的路线。

有一个Perl程序,s2p,它会将'sed'脚本转换为Perl。

基本的Perl结构如下:

my $atfirst = 0;
my $atend = 0;
while (<>)
{
    last if $atend;
    $atfirst = 1 if m/12:52:33/;
    $atend = 1 if m/12:59:33/;
    if ($atfirst)
    {
        process line as required
    }
}

请注意,编写代码时,代码将处理与结束标记匹配的第一行。如果您不想这样,请在测试后移动“最后一个”。

答案 3 :(得分:0)

如果您的日志文件按天隔离,则可以将时间戳转换为秒并进行比较。 (如果没有,请使用my answer to a question you asked earlier中的技术。)

说你的日志是

12:52:32 outside
12:52:43 strictly inside
12:59:33 end
12:59:34 outside

然后用

#! /usr/bin/perl

use warnings;
use strict;

my $LOGPATH = "/tmp/foo.log";

sub usage { "Usage: $0 start-time end-time\n" }

sub to_seconds {
  my($h,$m,$s) = split /:/, $_[0];
  $h * 60 * 60 +
       $m * 60 +
            $s;
}

die usage unless @ARGV == 2;
my($start,$end) = map to_seconds($_), @ARGV;

open my $log, "<", $LOGPATH or die "$0: open $LOGPATH: $!";
while (<$log>) {
  if (/^(\d+:\d+:\d+)\s+/) {
    my $time = to_seconds $1;
    print if $time >= $start && $time <= $end;
  }
  else {
    warn "$0: $LOGPATH:$.: no timestamp!\n";
  }
}

你会得到以下输出:

$ ./between 12:52:33 12:59:33
12:52:43 strictly inside
12:59:33 end