Как удалить элемент из массива Perl после его обработки? - PullRequest
3 голосов
/ 03 февраля 2010

Я читаю почтовый файл журнала postfix в массив, а затем зацикливаюсь на нем для извлечения сообщений.При первом проходе я проверяю соответствие в строке «to =» и получаю идентификатор сообщения.После создания массива MSGID я зацикливаюсь на массиве для извлечения информации о строках to =, from = и client =.

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

Есть предложения?Это на Perl.


Редактировать: приведенного ниже ответа gbacon было достаточно, чтобы заставить меня перейти на твердый раствор.Вот в чем суть:

my %msg;
while (<>) {
    my $line = $_;
    if (s!^.*postfix/\w+\[.+?\]: (\w+):\s*!!) {
            my $key = $1;
            push @{ $msg{$key}{$1} } => $2
                    while /\b(to|from|client|size|nrcpt)=<?(.+?)(?:>|,|\[|$)/g;
    }
    if ($line =~ s!^(\w+ \d+ \d+:\d+:\d+)\s(\w+.*)\s+postfix/\w+\[.+?\]: (\w+):\s*removed!!) {
            my $key = $3;
            push @{ $msg{$key}{date} } => $1;
            push @{ $msg{$key}{server} } => $2;
    }
}

use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%msg;

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

Спасибо всем, кто ответил.

Ответы [ 6 ]

5 голосов
/ 03 февраля 2010

Сделайте это за один проход:

#! /usr/bin/perl

use warnings;
use strict;

# for demo only
*ARGV = *DATA;

my %msg;
while (<>) {
  if (s!^.*postfix/\w+\[.+?\]: (\w+):\s*!!) {
    my $key = $1;
    push @{ $msg{$key}{$1} } => $2
      while /\b(to|from|client)=(.+?)(?:,|$)/g;
  }
}

use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%msg;
__DATA__
Apr  8 14:22:02 MailSecure03 postfix/smtpd[32388]: BA1CE38965: client=mail.example.com[x.x.x.x]
Apr  8 14:22:03 MailSecure03 postfix/cleanup[32070]: BA1CE38965: message-id=<49dc4d9a.6020...@example.com>
Apr  8 14:22:03 MailSecure03 postfix/qmgr[19685]: BA1CE38965: from=<mailt...@example.com>, size=1087, nrcpt=2 (queue active)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32608]: BA1CE38965: to=<us...@test.com>, relay=127.0.0.1[127.0.0.1]:10025, delay=1.7, delays=1/0/0/0.68, dsn=2.0.0, status=sent (250 OK, sent 49DC509B_360_15637_162D8438973)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32608]: BA1CE38965: to=<us...@test.com>, relay=127.0.0.1[127.0.0.1]:10025, delay=1.7, delays=1/0/0/0.68, dsn=2.0.0, status=sent (250 OK, sent 49DC509B_360_15637_162D8438973)
Apr  8 14:22:04 MailSecure03 postfix/qmgr[19685]: BA1CE38965: removed
Apr  8 14:22:04 MailSecure03 postfix/smtpd[32589]: 62D8438973: client=localhost.localdomain[127.0.0.1]
Apr  8 14:22:04 MailSecure03 postfix/cleanup[32080]: 62D8438973: message-id=<49dc4d9a.6020...@example.com>
Apr  8 14:22:04 MailSecure03 postfix/qmgr[19685]: 62D8438973: from=<mailt...@example.com>, size=1636, nrcpt=2 (queue active)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32417]: 62D8438973: to=<us...@test.com>, relay=y.y.y.y[y.y.y.y]:25, delay=0.19, delays=0.04/0/0.04/0.1, dsn=2.6.0, status=sent (250 2.6.0  <49dc4d9a.6020...@example.com> Queued mail for delivery)
Apr  8 14:22:04 MailSecure03 postfix/smtp[32417]: 62D8438973: to=<us...@test.com>, relay=y.y.y.y[y.y.y.y]:25, delay=0.19, delays=0.04/0/0.04/0.1, dsn=2.6.0, status=sent (250 2.6.0  <49dc4d9a.6020...@example.com> Queued mail for delivery)
Apr  8 14:22:04 MailSecure03 postfix/qmgr[19685]: 62D8438973: removed

Код работает, сначала ища идентификатор очереди ( например, , BA1CE38965 и 62D8438973 выше), который мы храним в $key.

Далее мы находим все совпадения в текущей строке (благодаря переключателю /g), которые выглядят как to=<...>, client=mail.example.com и т. Д., С разделительной запятой и без нее.

Обратите внимание на шаблон

  • \b - совпадения только на границе слова (предотвращает сопоставление xxxto=<...>)
  • (to|from|client) - совпадение to или from или client
  • (.+?) - сопоставляет значение поля с не жадным квантификатором
  • (?:,|$) - соответствует запятой или в конце строки без ввода в $3

Нежадный (.+?) заставляет матч останавливаться на первой запятой, с которой он сталкивается, а не на последней. В противном случае на линии с

to=<foo@example.com>, other=123

вы получите <foo@example.com>, other=123 в качестве получателя!

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

$VAR1 = {
  '62D8438973' => {
    'client' => [
      'localhost.localdomain[127.0.0.1]'
    ],
    'to' => [
      '<us...@test.com>',
      '<us...@test.com>'
    ],
    'from' => [
      '<mailt...@example.com>'
    ]
  },
  'BA1CE38965' => {
    'client' => [
      'mail.example.com[x.x.x.x]'
    ],
    'to' => [
      '<us...@test.com>',
      '<us...@test.com>'
    ],
    'from' => [
      '<mailt...@example.com>'
    ]
  }
};

Теперь предположим, что вы хотите напечатать всех получателей сообщения, чей идентификатор очереди BA1CE38965:

my $queueid = "BA1CE38965";
foreach my $recip (@{ $msg{$queueid}{to} }) {
  print $recip, "\n":
}

Может быть, вы хотите знать только, сколько получателей:

print scalar @{ $msg{$queueid}{to} }, "\n";

Если вы хотите предположить, что каждое сообщение имеет только одного клиента, откройте его с помощью

print $msg{$queueid}{client}[0], "\n";
4 голосов
/ 03 февраля 2010

Это на самом деле не ускорит обработку, так как удаление из середины массива - дорогостоящая операция.

Лучшие варианты:

  • Делай все за один проход
  • Когда вы создаете массив идентификаторов, включайте указатели (действительно, индексы) в основной массив, чтобы вы могли быстро получить доступ к его элементам для данного идентификатора
1 голос
/ 03 февраля 2010

Почему бы не сделать это:

my @extracted = map  extract_data($_), 
                grep msg_rcpt_to( $rcpt, $_ ), @log_data;

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

0 голосов
/ 03 февраля 2010

Общие методы для манипулирования содержимым массива:

# start over with this list for each example:
my @list = qw(a b c d);

сращивания

splice @list, 2, 1, qw(e);
# @list now contains: qw(a b e d)

pop и unshift :

pop @list;
# @list now contains: qw(a b c)

unshift @list;
# @list now contains: qw(b c d)

карта

@list = map { $_ eq 'b' ? () : $_ } @list;
# list now contains: qw(a c d);

ломтики массива :

@list[3..4] = qw(e f);
# list now contais: qw(a b c e f);

для и foreach петли:

foreach (@list)
{
    # $_ is aliased to each element of the list in turn;
    # assignments will be propogated back to the original structure
    $_ = uc if m/[a-c]/;
}
# list now contains: qw(A B C d);

Прочтите обо всех этих функциях по адресу perldoc perlfunc , срезы в perldoc perldata и для циклов в perldoc perlsyn .

0 голосов
/ 03 февраля 2010

Если у вас есть индекс под рукой, используйте сплайс:

splice(@array, $indextoremove, 1)

Но будь осторожен. Ваш индекс будет недействительным, как только вы удалите элемент.

0 голосов
/ 03 февраля 2010

В perl вы можете использовать процедуру splice () для удаления элементов из массива.

Как обычно, будьте осторожны при удалении из массива при циклическом перемещении по массиву, так как ваши индексы массива изменятся.

...