Perl, как регулярное выражение частей данных вместо всей строки, а затем распечатать CSV-файл - PullRequest
0 голосов
/ 30 июля 2010

У меня есть работающий Perl-скрипт, который захватывает нужные мне данные и отображает их в STDOUT, но теперь мне нужно изменить его на создать файл данных (csv, tab dellimited, любой файл разделителя) .Регулярное выражение фильтрует данные, которые мне нужны, но я не хочу всю строку, просто фрагменты вывода.Я предполагаю, что мне нужно было бы сохранить это в другой переменной для создания моего выходного файла.

Мне нужен хороший пример этого или предложения по изменению этого кода.Заранее спасибо.: -)

Вот мой код:

#!/usr/bin/perl -w
# Usage: ./bakstatinfo.pl Jul 28 2010 /var/log/mybackup.log <server1> <server2>

use strict;
use warnings;

#This piece added to view the arguments passed in
$" = "][";
print "===================================================================================\n";
print "[@ARGV]\n";

#Declare Variables
my($mon,$day,$year,$file,$server) = @ARGV;
my $regex_flag = 0;                 

splice(@ARGV, 0, 4, ());            

foreach my $server ( @ARGV ) {      #foreach will take Xn of server entries and add to the loop
    print "===================================================================================\n";
    print "REPORTING SUMMARY for SERVER : $server\n";
    open(my $fh,"ssh $server cat $file |") or die "can't open log $server:$file: $!\n";
    while (my $line = <$fh>) {
        if ($line =~ m/.* $mon $day \d{2}:\d{2}:\d{2} $year:.*(ERROR:|backup-date=|backup-size=|backup-time=|backup-status)/) {
            print $line;
            $regex_flag=1; #Set to true
        }
    }
        if ($regex_flag==0) { 
           print "NOTHING TO REPORT FOR $server: $mon $day $year \n";
        }
    $regex_flag=0; 
    close($fh);
}

Образец необработанного файла журнала, который я использую: (недавно добавлен для лучшего представления журнала)

Tue Jul 27 23:00:06 2010: test202.bak_lvm:backup:ERROR: mybak-abc appears to be already running for this backupset
Tue Jul 27 23:00:06 2010: test202.bak_lvm:backup:ERROR: If you are sure mybak-abc is not running, please remove the file /etc/mybak-abc/test202.bak_lvm/.mybak-abc.pid and restart mybak-abc
Tue Jul 27 23:00:06 2010: test202.bak_lvm:backup:INFO: PHASE START: Cleanup
Tue Jul 27 23:00:06 2010: test202.bak_lvm:backup:INFO: PHASE END: Cleanup
Tue Jul 27 23:00:06 2010: test202.bak_lvm:backup:INFO: END OF BACKUP
Wed Jul 28 00:00:04 2010: db9.abc.bak:backup:INFO: START OF BACKUP
Wed Jul 28 00:00:04 2010: db9.abc.bak:backup:INFO: PHASE START: Initialization
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:WARNING: Binary logging is off.
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: License check successful
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: License check successful for lvm-snapshot.pl
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-set=db9.abc.bak
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-date=20100728000004
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: SQL-server-os=Linux/Unix
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-type=regular
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: host=db9.abc.bak.test.com
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-date-epoch=1280300404
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: retention-policy=3D
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: mybak-abc-version=ABC for SQL Enterprise Edition - version 3.1
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: SQL-version=5.1.32-test-SMP-log
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-directory=/home/backups/db9.abc.bak/20100728000004
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-level=0
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: backup-mode=raw
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: PHASE END: Initialization
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: PHASE START: Running pre backup plugin
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: PHASE START: Flushing logs
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: PHASE END: Flushing logs
Wed Jul 28 00:00:05 2010: db9.abc.bak:backup:INFO: PHASE START: Creating snapshot based backup
Wed Jul 28 00:00:11 2010: db9.abc.bak:backup:INFO: Wed Jul 28 00:49:53 2010: test203.bak_lvm:backup:INFO: raw-databases-snapshot=test SQL sgl 
Wed Jul 28 00:49:53 2010: test203.bak_lvm:backup:INFO: PHASE END: Creating snapshot based backup
Wed Jul 28 00:49:53 2010: test203.bak_lvm:backup:INFO: PHASE START: Calculating backup size & checksums 
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: last-backup=/home/backups/test203.bak_lvm/20100726200004
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: backup-size=417.32 GB
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: PHASE END: Calculating backup size & checksums 
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: read-locks-time=00:00:05
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: flush-logs-time=00:00:00
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: backup-time=04:49:51
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: backup-status=Backup succeeded

Мойрабочий вывод сейчас:

===================================================================================
[Jul][28][2010][/var/log/mybackup.log][server1]
===================================================================================
REPORTING SUMMARY for SERVER : server1
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: backup-size=417.32 GB
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: backup-time=04:49:51
Wed Jul 28 00:49:54 2010: test203.bak_lvm:backup:INFO: backup-status=Backup succeeded

Вывод Мне нужно увидеть, что будет выглядеть примерно так: (файл данных с разделителем ';', например)

MyDate=Wed Jul 28;MyBackupSet= test203.bak_lvm;MyBackupSize=187.24 GB;MyBackupTime=04:49:51;MyBackupStat=Backup succeeded

Ответы [ 2 ]

4 голосов
/ 30 июля 2010

Используйте «захватные скобки» для определения битов, с которыми вы хотите иметь дело.

   if ($line =~ m/(.* $mon $day) \d{2}:\d{2}:\d{2} $year:.*
                  (ERROR:|backup-date=|backup-size=|
                   backup-time=|backup-status)/x) {

Вам нужно будет сделать операцию над вторым набором скобок - теми, которые окружают начало различных ключевых слов. Возможно, вам придется нарезать их на кусочки внутри условия.

Когда все данные извлечены в переменные, используйте Text :: CSV для обработки вывода CSV (и ввода).

Существует множество модулей для обработки HTML или XML (более 2000, и я думаю, более 3000, с HTML в их названии - я случайно посмотрел вчера). Многие из них не применимы, но CPAN - ваш друг.


Ответы на вопросы, заданные комментариями

Могу ли я разделить их на отдельные переменные? Первая часть дает мне дату / время, которое мне нужно. Затем следующий фильтр дает мне 1) ошибка: 2) дата резервирования = 3) размер резервной копии = ... и т. Д.

Больше или меньше. К сожалению, вы не показываете некоторые репрезентативные строки ввода, что означает, что трудно сказать, что может быть лучше. Тем не менее, похоже, что такая схема, как:

while (my $line = <$fh>)
{
    chomp $line;
    if ($line =~ m/(.* $mon $day) \d\d:\d\d:\d\d $year:/)
    {
        my $date = $1;
        my %items = ();
        $line =~ s/.* $mon $day \d\d:\d\d:\d\d $year://;
        while ($line =~ m/(ERROR|backup-date|backup-size|
                           backup-time|backup-status)
                          [:=]([^:]+)/x)
        {
            my $key = $1;
            my $val = $2;
            $items{$key} = $val;
            $line =~ s/$key[:=]$val[:=]?//;
        }
        # The %items hash contains the split out information.
        # Now write the data for this line of the log file.
    }
}

Возможно, есть более эффективные способы обработки обрезки (но это Perl, так что TMTOWTDI), но основная идея здесь состоит в том, чтобы поймать интересные строки, а затем постепенно вырезать интересующие фрагменты из строки, поэтому строка уменьшается на каждой итерации (следовательно, в конечном итоге завершается внутренний цикл while).

Обратите внимание на использование модификатора /x, чтобы обеспечить более удобочитаемое разделение на регулярные выражения (я тоже отредактировал исходную версию ответа). Я также допустил, чтобы за «ОШИБКОЙ» следовал «=», а за другими ключевыми словами следовало бы «:»; кажется маловероятным, что вы получите ложные совпадения таким образом, и это упрощает операции замены регулярных выражений. Первоначальное сопоставление с образцом больше не требует наличия одного из подразделов. Вы должны самостоятельно оценить, имеют ли значение эти небольшие изменения (которые могут привести к несоответствующей информации) или нет. Для большинства моих целей вероятность несоответствия достаточно мала, чтобы не быть проблемой, но по юридическим причинам она может быть неприемлемой для вас.


Отвечая на вопросы, заданные 'answer'

Я произвел некоторые данные:

Wed Jul 30 00:49:51 2010: test203.bak_lvm:backup:INFO: backup-size=417.32 GB
Wed Jul 30 00:49:52 2010: test203.bak_lvm:backup:INFO: backup-time=04:49:51
Wed Jul 30 00:49:53 2010: test203.bak_lvm:backup:INFO: backup-status=Backup succeeded
Wed Jul 30 00:49:51 2010: backup-size=417.32 GB:backup-time=04:49:51:backup-status=Backup succeeded

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

use strict;
use warnings;
use constant debug => 0;
my $mon = 'Jul';
my $day = 30;
my $year = 2010;

while (my $line = <>)
{
    chomp $line;
    print "Line: $line\n" if debug;
    if ($line =~ m/(.* $mon $day) \d\d:\d\d:\d\d $year:/) #Mon Jul 26 22:00:02 2010:
    {
        print "### Scan\n";
        my $date = $1;
        print "$date\n";
        my %items = ();
        $line =~ s/.* $mon $day \d\d:\d\d:\d\d $year://;
        print "Line: $line\n" if debug;
        while ($line =~ m/(ERROR|backup-date|backup-size|backup-time|backup-status)[:=]([^:]+)/)
        {
            my $key = $1;
            my $val = $2;
            $items{$key} = $val;
            $line =~ s/$key[:=]$val[:=]?//;
            print "$key=$val\n";
            print "Line: $line\n" if debug;
        }
        print "### Verify\n";
        for my $key (sort keys %items)
        {
            print "$key = $items{$key}\n";
        }
    }
}

Вывод, который я получаю:

### Scan
Wed Jul 30
backup-size=417.32 GB
### Verify
backup-size = 417.32 GB
### Scan
Wed Jul 30
backup-time=04
### Verify
backup-time = 04
### Scan
Wed Jul 30
backup-status=Backup succeeded
### Verify
backup-status = Backup succeeded
### Scan
Wed Jul 30
backup-size=417.32 GB
backup-time=04
backup-status=Backup succeeded
### Verify
backup-size = 417.32 GB
backup-status = Backup succeeded
backup-time = 04

Цикл проверки выводит данные из хэша '%items' довольно счастливо. Когда значение отладки установлено в 1 вместо 0, я получаю вывод:

Line: Wed Jul 30 00:49:51 2010: test203.bak_lvm:backup:INFO: backup-size=417.32 GB
### Scan
Wed Jul 30
Line:  test203.bak_lvm:backup:INFO: backup-size=417.32 GB
backup-size=417.32 GB
Line:  test203.bak_lvm:backup:INFO: 
### Verify
backup-size = 417.32 GB
Line: Wed Jul 30 00:49:52 2010: test203.bak_lvm:backup:INFO: backup-time=04:49:51
### Scan
Wed Jul 30
Line:  test203.bak_lvm:backup:INFO: backup-time=04:49:51
backup-time=04
Line:  test203.bak_lvm:backup:INFO: 49:51
### Verify
backup-time = 04
Line: Wed Jul 30 00:49:53 2010: test203.bak_lvm:backup:INFO: backup-status=Backup succeeded
### Scan
Wed Jul 30
Line:  test203.bak_lvm:backup:INFO: backup-status=Backup succeeded
backup-status=Backup succeeded
Line:  test203.bak_lvm:backup:INFO: 
### Verify
backup-status = Backup succeeded
Line: Wed Jul 30 00:49:51 2010: backup-size=417.32 GB:backup-time=04:49:51:backup-status=Backup succeeded
### Scan
Wed Jul 30
Line:  backup-size=417.32 GB:backup-time=04:49:51:backup-status=Backup succeeded
backup-size=417.32 GB
Line:  backup-time=04:49:51:backup-status=Backup succeeded
backup-time=04
Line:  49:51:backup-status=Backup succeeded
backup-status=Backup succeeded
Line:  49:51:
### Verify
backup-size = 417.32 GB
backup-status = Backup succeeded
backup-time = 04

Операции замены удаляют ранее согласованную часть строки. Существуют способы продолжения матча, на котором вы остановились - см. \G на странице 'perlre' .

Обратите внимание, что регулярное выражение создано для остановки на первом двоеточии после слова "двоеточие или равно" после ключевого слова. Это означает, что оно сокращает время резервного копирования. Один мораль - «не используйте разделитель, который может появляться в данных». Другой - «предоставьте образцы данных, чтобы люди могли вам легче помочь». Другой - «предоставляйте полные, но минимальные рабочие скрипты, где это возможно».


Обработка данных образца

Теперь, когда у нас есть пример входных данных, мы можем видеть, что вам нужна немного другая обработка. Этот скрипт:

use strict;
use warnings;
use constant debug => 0;
my $mon = 'Jul';
my $day = 28;
my $year = 2010;
my %items = ();

while (my $line = <>)
{
    chomp $line;
    print "Line: $line\n" if debug;
    if ($line =~ m/(.* $mon $day) \d\d:\d\d:\d\d $year: ([^:]+):backup:/) #Mon Jul 26 22:00:02 2010:
    {
        print "### Scan\n" if debug;
        my $date = $1;
        my $set = $2;
        print "$date ($set): " if debug;
        $items{$set}->{'a-logdate'} = $date;
        $items{$set}->{'a-dataset'} = $set;
        if ($line =~ m/(ERROR|backup-date|backup-size|backup-time|backup-status)[:=](.+)/)
        {
            my $key = $1;
            my $val = $2;
            $items{$set}->{$key} = $val;
            print "$key=$val\n" if debug;
        }
    }
}

print "### Verify\n";
for my $set (sort keys %items)
{
    print "Set: $set\n";
    my %info = %{$items{$set}};
    for my $key (sort keys %info)
    {
        printf "%s=%s;", $key, $info{$key};
    }
    print "\n";
}

дает этот результат в файле данных примера.

### Verify
Set: db9.abc.bak
a-dataset=db9.abc.bak;a-logdate=Wed Jul 28;backup-date=20100728000004;
Set: test203.bak_lvm
a-dataset=test203.bak_lvm;a-logdate=Wed Jul 28;backup-size=417.32 GB;backup-status=Backup succeeded;backup-time=04:49:51;

Обратите внимание, что теперь у нас есть примеры данных, и мы видим, что в строке имеется только одна пара ключ / значение, но в день выполняется резервное копирование нескольких систем.Таким образом, внутренний цикл while становится простым if.Распечатка происходит в конце.И я использую «двухуровневый» хэш.%items содержит запись для каждого набора данных;запись, однако, является ссылкой на хеш.Новичкам необязательно что-то играть, но с предыдущим кодом это стало совершенно естественным.Также обратите внимание, что эта версия не взламывает строки - в этом нет необходимости, поскольку в каждой строке содержится только одна партия данных.

Можно ли ее улучшить, да, несомненноЭто работает?Да, более или менее ... Можно ли это взломать в форму?Да, его можно взломать, чтобы он работал как вам нужно.

0 голосов
/ 30 июля 2010

@ Джонатан - я записал текстовый файл в цикле while. Вроде работает. Я попытался сделать это после второго цикла while, как вы предложили в своем комментарии. Я не уверен, почему это не сработало.

open (my $MYDATAFILE, ">/home/test/myout.txt") || die "cannot append $!";
open(my $fh,"ssh $server cat $file |") or die "can't open log $server:$file: $!\n";
while (my $line = <$fh>)
{
    chomp $line;
    if ($line =~ m/(.* $mon $day) \d\d:\d\d:\d\d $year:/) #Mon Jul 26 22:00:02 2010:
    {
        my $date = $1;
        #print $date;
        my %items = ();
        $line =~ s/.* $mon $day \d\d:\d\d:\d\d $year://;
        while ($line =~ m/(ERROR|backup-date|backup-size|backup-time|backup-status)[:=]([^:]+)/)
        {
            my $key = $1;
            my $val = $2;
            $items{$key} = $val;
            $line =~ s/$key[:=]$val[:=]?//;
            #print "[$key]";
            #print "[$val]";
            print $MYDATAFILE "$key=$val";
        }
        # The %items hash contains the split out information.
        # Now write the data for this line of the log file.
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...