我正在将一个postfix邮件日志文件读入一个数组,然后循环遍历它以提取消息。在第一遍,我正在检查“to =”行上的匹配并获取消息ID。在构建一个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;
我确信第二个正则表达式可以更令人印象深刻,但它可以完成我需要的工作。我现在可以获取所有消息的哈希值并提取出我感兴趣的消息。
感谢所有回答的人。
答案 0 :(得分:5)
一次性完成:
#! /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
中的队列ID(例如,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
放在数组的末尾(例如,因为可能有多个收件人)连接到队列ID和字段名称。看看结果:
$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>' ] } };
现在说您要打印队列ID为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";
答案 1 :(得分:4)
它实际上不会使处理更快,因为从阵列中间移除是一项昂贵的操作。
更好的选择:
答案 2 :(得分:1)
为什么不这样做:
my @extracted = map extract_data($_),
grep msg_rcpt_to( $rcpt, $_ ), @log_data;
完成后,您将获得一系列提取的数据,其顺序与日志中显示的顺序相同。
答案 3 :(得分:0)
在perl中,您可以使用splice()例程从数组中删除元素。
像往常一样,在循环数组时从数组中删除时要小心,因为数组索引会发生变化。
答案 4 :(得分:0)
假设您手头有索引,请使用splice:
splice(@array, $indextoremove, 1)
但要小心。删除元素后,您的索引将无效。
答案 5 :(得分:0)
操纵数组内容的常用方法:
# 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中的循环中了解所有这些功能。