Как искать строки в файле между двумя метками времени с помощью Perl?

В Perl я пытаюсь считать файл журнала и распечатаю только строки, которые имеют метку времени между двумя определенными разами. Формат времени является hh:mm:ss, и это всегда - третье значение на каждом журнале. Например, я искал бы строки, которые упадут между 12:52:33 на 12:59:33

Я плохо знаком с Perl и понятия не имею который маршрут взять, чтобы даже начать программировать это. Я вполне уверен, это использовало бы некоторый тип regex, но ни за что в жизни я не могу даже начать понимать, каково это было бы. Мог кто-то помогать мне с этим.

Кроме того, для создания этого более трудным, я должен сделать это с базовыми модулями Perl, потому что моя компания не позволит мне использовать любые другие модули, пока они не были протестированы и проверены не будет никаких вредных воздействий ни на одну из систем, с которыми может взаимодействовать сценарий.

5
задан Matt Pascoe 28 June 2010 в 18:00
поделиться

4 ответа

В псевдокоде вы сделаете примерно следующее:

  • прочитайте файл построчно:
    • разбираем временную метку для этой строки.
    • если она меньше времени начала, перейдите к следующей строке.
    • если она больше времени окончания, перейдите к следующей строке!
    • else: это нужная вам строка: распечатайте ее.

Возможно, это слишком продвинуто для ваших нужд, но оператор флип-флоп сразу приходит на ум как нечто полезное.

Для чтения файла из 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;
}
2
ответ дан 14 December 2019 в 18:56
поделиться

Если время начала и окончания известно, вам понадобится однострочный 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/;
}

Как отмечено в комментарии Ether, это не удастся, если точное время не указано. Если это возможно, вместо этого можно реализовать следующую логику:

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
ответ дан 14 December 2019 в 18:56
поделиться

Если каждая строка в файле имеет отметку времени, то в '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
    }
}

Обратите внимание, что как написано, код будет обрабатывать первую строку, которая соответствует конечному маркеру. Если вы этого не хотите, переместите «последний» после теста.

1
ответ дан 14 December 2019 в 18:56
поделиться

Если ваши файлы журналов разделены по дням, вы можете преобразовать метки времени в секунды и сравнить их. (Если нет, воспользуйтесь техникой из моего ответа на вопрос, который вы задали ранее .)

Скажите, что ваш журнал

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
0
ответ дан 14 December 2019 в 18:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: