Удаление линий вокруг заданной совпадающей линии - PullRequest
0 голосов
/ 23 октября 2018

У меня есть файл журнала, который выглядит следующим образом:

2018/10/08 17:11:28 [debug] 8851#0: *2 Sent 8/8 bytes.
2018/10/08 17:11:28 [debug] 8851#0: *2 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Receiving 8 bytes
2018/10/08 17:11:33 [debug] 8851#0: *36 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Handling TRL request #0001: [GET_REGION_INFO].
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 8/8 bytes.
2018/10/08 17:11:33 [debug] 8851#0: *36 Finished processing TRL request #0001.
2018/10/08 17:11:33 [debug] 8851#0: *36 GET_REGION_INFO: Staging 99 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 99/99 bytes.
2018/10/08 17:11:33 [debug] 8851#0: *36 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 8/8 bytes.
2018/10/08 17:11:38 [debug] 8851#0: *22 Receiving 8 bytes
2018/10/08 17:11:38 [debug] 8851#0: *22 Session: Staging 8 bytes in thread buffer.

На основе строки, содержащей [GET_REGION_INFO], я хочу удалить все строки рядом с ним с тем же идентификатором запроса (*36в этом случае) .. скажем, в пределах 10 строк в любом направлении.

То, что я до сих пор получил, это ... это работает с отсканированной строки ... Проблема в том, что я даже не думаю, что этот базовый подход вообще сработает, чтобы получить соответствующие строкинад ним.(Они уже были напечатаны с одной стороны)

perl -lane '$requestId=$F[4] if /\[GET_REGION_INFO\]/;$requestId="Z" if $requestId ne $F[4]; print if $requestId eq "Z";' error.log

Единственные подходы, которые я могу придумать, кажутся подверженными ошибкам и чрезмерно сложными.Этот файл имеет размер около гигабайта, так что вся эта хрень - это то, чего я хотел бы , чтобы избежать ... хотя машина имеет 32 ГБ, так что ...

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

Я должен отметить, что *36 является идентификатором запроса (как указывает переменная), и теоретически несколько запросов могут смешиваться.Но это редко, поэтому нормально, если сценарий не может удалить непоследовательные строки (например, мой текущий сценарий).

О, еще одна вещь. Идентификаторы запросов в конечном итоге перерабатываются, поэтому я не могу сделатьчто-нибудь умное, например, создать их список и затем проанализировать весь файл с этим списком.

И ожидаемый результат, учитывая пример ввода:

2018/10/08 17:11:28 [debug] 8851#0: *2 Sent 8/8 bytes.
2018/10/08 17:11:28 [debug] 8851#0: *2 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:38 [debug] 8851#0: *22 Receiving 8 bytes
2018/10/08 17:11:38 [debug] 8851#0: *22 Session: Staging 8 bytes in thread buffer.

1 Ответ

0 голосов
/ 23 октября 2018

Проблема: в некоторых строках есть определенная фраза с определенным идентификатором запроса.Любые строки для этого идентификатора запроса, которые находятся на заданном расстоянии от строки с фразой, должны быть пропущены, так же как и сама строка.

Мы не знаем, для какого идентификатора запроса это происходит, чтотакже может меняться через файл.

Сначала прочитайте, пока фраза не появится, сохраняя строки в буфере.Как только мы анализируем идентификатор запроса из этой строки, мы знаем, какой это.Затем обработайте накопленный буфер, печатая все строки для других идентификаторов запроса и только те, которые больше, чем расстояние пропуска для «выбранного».

Затем продолжайте собирать строки в буфере, пока фраза не будет найдена снова.Обработка буфера: выведите все строки для других идентификаторов и только те, которые находятся достаточно далеко от обеих фраз для выбранного идентификатора.

Приведенный ниже код был протестирован с использованием вариантов опубликованных данных.Он учитывает возможное смешивание строк с разными идентификаторами запроса;отбрасывание этого, предполагая, что строки с любым идентификатором запроса всегда находятся в блоке, значительно упростит код и ускорит его.

use warnings;
use strict;
use feature 'say';

my ($file, $skip_dist) = @ARGV;
die "Usage: $0 log-file [skip-distance]\n" if not $file;    
$skip_dist //= 2;  #/

my $trigger = qr{\[GET_REGION_INFO\]};

open my $fh, '<', $file  or die "Can't open $file: $!";

my (@buf, $req_mark, $skip_idx, $next_req_cnt);

while (<$fh>) {    
    if (not $req_mark and /$trigger/) {
        # Find the req_id of interest and save it into req_mark,
        # then process the accumulated buffer
        my ($req_id, $msg) = /:\s+(\*[0-9]+)\s+(.*)/;
        $req_mark = $req_id;

        # Find position of req_id which is skip_dist before the mark
        # and print lines for req_mark before it (and all others)
        my $del_idx = find_skip_start($req_mark, \@buf, $skip_dist);
        for my $i (0..$#buf) {
            if ($skip_idx and $i < $skip_idx) { print $buf[$i] }
            else {
                my ($req_id) = $buf[$i] =~ /:\s+(\*[0-9]+)/;
                print $buf[$i] if $req_id ne $req_mark;
            }
        }
        @buf = (); 
        $skip_idx = 0;
    }
    elsif (/$trigger/ or eof) { 
        # Process buffer collected between previous and this trigger,
        # Or up to the end of file (the last line then need be added)
        push @buf, $_  if eof;
        my $skip_idx = (not eof) 
            ? find_skip_start($req_mark, \@buf, $skip_dist) 
            : $#buf+1;
        for my $i (0..$#buf) { 
            my ($req_id) = $buf[$i] =~ /:\s+(\*[0-9]+)/;
            print $buf[$i]
                if  $req_id ne $req_mark
                or (++$next_req_cnt > $skip_dist and $i < $skip_idx);
        }
        @buf = (); 
        $next_req_cnt = $skip_idx = 0;

        # Check whether the request-id changed and update for next buffer
        my ($req_id) = /:\s+(\*[0-9]+)/;
        if ($req_id ne $req_mark) {
            $req_mark = $req_id 
        }

    }   
    else { push @buf, $_ }
}

sub find_skip_start {
    my ($req_mark, $buf, $skip_dist) = @_;
    my ($skip_idx, $prev_req_cnt);
    for my $i (0..$#$buf) {
        my ($req_id) = $buf->[$#$buf-$i] =~ /:\s+(\*[0-9]+)/;
        if ( $req_id eq $req_mark    and
            (++$prev_req_cnt >= $skip_dist) )
        {
            $skip_idx = $#$buf-$i;
            last;
        }
    }
    return $skip_idx;
}

Есть ряд мест, где можно повысить эффективность.

Главный вопрос эффективности касается центральной проблемы размера буфера.Сколько данных можно собрать, пока мы не нажмем на курок?Что, если в файле всего несколько [GET_..] строк, и в итоге мы накапливаем гигабайт (ы) данных?Тогда было бы лучше время от времени осветлять (печатать часть) буфер;но если данных никогда не бывает много, то частичное опустошение буфера сильно усложняет ситуацию, поскольку фраза может быть близка (сколько строк еще нужно сохранить?).

На этот вопрос нельзя ответить, не зная частоты этогофразу, а также частоту идентификатора запроса.Тогда одной оптимизацией было бы сначала заглянуть в файл, оценить эти частоты и принять решение по этому вопросу.Однако это не слишком надежно, поскольку нет никакой гарантии, что файл журнала в любом случае будет согласованным.

Приведенный выше код явно предполагает, что данных никогда не будет слишком много, и поэтому мы не будемвызвать проблемы, но было бы разумно добавить проверку и выписать часть буфера, если он становится слишком большим.


Для записи, когда я запускаю вышеупомянутую программу с образцами данных OP, получается:

2018/10/08 17:11:28 [debug] 8851#0: *2 Sent 8/8 bytes.
2018/10/08 17:11:28 [debug] 8851#0: *2 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 GET_REGION_INFO: Staging 99 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 99/99 bytes.
2018/10/08 17:11:33 [debug] 8851#0: *36 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 8/8 bytes.
2018/10/08 17:11:38 [debug] 8851#0: *22 Receiving 8 bytes
2018/10/08 17:11:38 [debug] 8851#0: *22 Session: Staging 8 bytes in thread buffer.

В данном примере введите идентификатор запроса в строке с[GET..] ($req_mark) равно *36.

По умолчанию количество пропускаемых строк *36 (вокруг строки [GET..]) установлено в два (2) для лучшего тестирования;это можно изменить при вызове.

Нет строк *36 перед строкой с [GET...] (точнее, там, где была эта строка), так как в данных было только две, и они должным образомпропускаются;и первые две строки с *36 после строки [GET..] (там, где была эта строка) опущены, затем остальная часть печатается.Это ожидаемый результат.

Когда я задаю расстояние пропуска (настройка $skip_dist) 10, вывод будет

2018/10/08 17:11:28 [debug] 8851#0: *2 Sent 8/8 bytes.
2018/10/08 17:11:28 [debug] 8851#0: *2 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:38 [debug] 8851#0: *22 Receiving 8 bytes
2018/10/08 17:11:38 [debug] 8851#0: *22 Session: Staging 8 bytes in thread buffer.

, как и ожидалось: до 10 строк с *36 обе дои после строки с [GET..] в этом примере данных, поэтому *36 строки не печатаются.


Исходное сообщение (при условии, что *36 является данным идентификатором запроса, представляющим интерес)

Сохраните эти *36 строки в буфере, и пока вы находитесь в этом регионе, проверьте эту фразу.Как только вы выйдете из этого региона, проверьте, была ли найдена фраза, и напечатайте соответственно

my $trigger     = 'GET_REGION_INFO';
my $region_mark = '*36';

my (@buff, $drop_lines_mark);

while (<$fh>) {
    my ($req_id, $msg) = /.*?:\s*(\*[0-9]+)\s+(.*)/;
    if ($req_id eq $region_mark) {
        push @buff, $_  
        $drop_lines_mark = $#buff  if $msg =~ /$trigger/;
    }
    elsif (@buff) {              # just left region of interest
        if ($drop_lines_mark) { 
            for my $i (0..$#buff) {
                print $buff[$i] 
                    if $i < $drop_lines_mark-10 
                    or $i > $drop_lines_mark+10;
            }
        }
        else { print for @buff }

        $drop_lines_mark = '';
        @buff = ();
        print;      # don't forget the current line
    }
    else { print }
}

Непроверенный код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...