我正在使用Perl脚本,我需要使用MTA的日志。以下是我想要使用的查询。
sh-3.2# cat /var/log/pmta/File_name-2017-03-23*|egrep 'email.domain.com'|cut -d, -f6|cut -d- -f1|sort|uniq -c
此查询的输出存储在$case8Q1
。
310 blk
1279 hrd
87 sft
144056 success
18 unk
正如您所看到的,查询给出了5个值,但情况并非总是如此。它也可以这样给出。因此行数可能每次都变化(2或3或4或最大5)
310 blk
144056 success
18 unk
下面是给出错误结果的示例代码
sub get_stats {
$case8Q1 =~ s/^\s+//;
@case8Q1_split = split( '\n', $case8Q1 );
@first_part = split( ' ', $case8Q1_split[0] );
@second_part = split( ' ', $case8Q1_split[1] );
@third_part = split( ' ', $case8Q1_split[2] );
@fourth_part = split( ' ', $case8Q1_split[3] );
@fifth_part = split( ' ', $case8Q1_split[4] );
if ( $first_part[1] eq 'blk' ) {
$report{Block} = $first_part[0];
}
elsif ( $first_part[1] eq 'hrd' ) {
$report{Hard} = $first_part[0];
}
elsif ( $first_part[1] eq 'sft' ) {
$report{Soft} = $first_part[0];
}
elsif ( $first_part[1] eq 'success' ) {
$report{Success} = $first_part[0];
}
elsif ( $first_part[1] eq 'unk' ) {
$report{Unknown} = $first_part[0];
}
# rest ifelse blocks so on........!
}
其中报告为哈希%report
。
有人可以帮助我如何从这里操作它。
我拥有所有的值,但如果我按照上面的正常if
- else
进行操作,则需要至少25个块。
如果不清楚,请告诉我。
来源日志示例:
b,email@aol.com,206.1.1.8,2017-03-23 00:01:11-0700,<14901.eb201.TCR2.338351.18567117907MSOSI1.152OSIMS@email.domain.com>,sft-routing-errors,4.4.4 (unable to route: dns lookup failure),
b,email@gmail.com,206.9.1.8,2017-03-23 00:02:13-0700,<149019.eb201.TCR2.338351.18567119237MSOSI1.152OSIMS@email.domain.com>,sft-no-answer-from-host,4.4.1 (no answer from host),
b,email@gmail.com,206.1.1.5,2017-03-23 03:43:36-0700,<149020.eb201.TCR2.338656.18570260933MSOSI1.152OSIMS@email.domain.com>,sft-server-related,4.3.2 (system not accepting network messages),smtp;421 Too many concurrent SMTP connections
b,email@yahoo.com,,2017-03-23 03:54:44-0700,<149019.eb201.TCR2.338351.18567013352MSOSI1.152OSIMS@email.domain.com>,sft-message-expired,4.4.7 (delivery time expired),
b,email@msn.com,206.1.1.1,2017-03-23 05:04:20-0700,<14902666.eb201.TCR2.3831.2620484MSOSI6374125.102OSIMS@email.domain.com>,hrd-invalid-mailbox,5.0.0 (undefined status),smtp;550 Requested action not taken: mailbox unavailable
b,email@msn.com,206.1.1.1,2017-03-23 05:04:20-0700,<14902666.eb201.TCR2.3831.2620484MSOSI6374125.102OSIMS@email.domain.com>,hrd-invalid-domain,5.0.0 (undefined status),smtp;550 Requested action not taken: mailbox unavailable
b,email@aol.com.com,66.1.1.1,2017-03-23 05:08:44-0700,<149021.eb201.KCR2.021089.566131285MSOSI1.89OSIMS@email.domain.com>,unk-other,4.0.0 (undefined status),smtp;451 Your domain is not configured to use this MX host.
b,email@gmail.com,206.1.1.1,2017-03-23 05:13:22-0700,<1490206.eb201.KCR2.6637.56206428MSOSI1.102OSIMS@email.domain.com>,blk-bad-connection,4.4.2 (bad connection),
b,email@qq.com.com,206.1.1.1,2017-03-23 05:13:22-0700,<1490206.eb201.KCR2.6637.56206428MSOSI1.102OSIMS@email.domain.com>,blk-spam-related,4.4.2 (bad connection),
这里的要求更进一步。我需要域名计数 - 例如
Date Domain Success Block Soft Hard Unknown
2017-03-23 gmail 1 1 1 1 1 1
2017-03-23 yahoo 1 1 1 1 1 1
2017-03-23 msn 1 1 1 1 1 1
2017-03-23 aol 1 1 1 1 1 1
2017-03-23 other domain 1 1 1 1 1 1
我的问题是其他域包含除gmail,yahoo,msn,hotmail和aol之外的所有域。 count 1就是一个例子,它可以是0。
答案 0 :(得分:2)
好的,所以 - 你开始这么做是非常困难的,因为...... perl
原则上可以做任何削减/排序/ uniq的事情。
如果没有一些示例输入,我无法为你重写,但......我认为你应该考虑这一点。
您也不应该使用全局变量,并使用my
的词法变量。
并且 - 正如您已经注意到的 - 如果您为变量名编号,那么您真的应该考虑使用数组。
这样的事情:
use Data::Dumper
my @stuff = map { [split] } split( "\n", $case8Q1 );
print Dumper \@stuff;
给你:
$VAR1 = [
[
'310',
'blk'
],
[
'1279',
'hrd'
],
[
'87',
'sft'
],
[
'144056',
'success'
],
[
'18',
'unk'
]
];
但是你可以更进一步,因为你根本不需要将其解析为数据结构:
my %data = reverse $case8Q1 =~ m/(\d+) (\w+)/g;
print Dumper \%data;
然后给你:
$VAR1 = {
'hrd' => '1279',
'sft' => '87',
'blk' => '310',
'unk' => '18',
'success' => '144056'
};
然后,您可以将其转换为您的报告&#39;通过再次使用,键值查找:
my %keyword_for = (
"blk" => "Block",
"hrd" => "Hard",
"sft" => "Soft",
"success" => "Success",
"unk" => "Unknown",
);
foreach my $key ( keys %data ) {
$report{$keyword_for{$key}} = $data{$key};
}
这会给你:
$VAR1 = {
'Soft' => '87',
'Unknown' => '18',
'Success' => '144056',
'Block' => '310',
'Hard' => '1279'
};
或者更进一步,使用map
:
my %report = map { m/(\d+) (\w+)/
&& $keyword_for{$2} // $2 => $1 } split "\n", $case8Q1;
print Dumper \%report;
正如你所说,你想要所有要填充的值....实际上,我建议不要这样做,并处理未定义的&#39;在使用类似的东西生成输出时正确:
my @field_order = qw ( Block Hard Soft Success Unknown this_field_missing );
print join "\t", @field_order,"\n";
print join "\t", ( map { $report{$_} // 0 } @field_order),"\n";
通过这种方式,您可以获得定义顺序输出,其中哈希不要执行定义顺序。这给出了:
Block Hard Soft Success Unknown this_field_missing
310 1279 87 144056 18 0
但是如果你真的想用零值回填空哈希:
$report{$_} //= 0 for values %keyword_for;
但是,现在您已经发布了一些日志来解决您的问题 - 问题的很多更简单:
#!/usr/bin/env perl
use strict;
use warnings;
#configure it:
my %keyword_for = (
"blk" => "Block",
"hrd" => "Hard",
"sft" => "Soft",
"success" => "Success",
"unk" => "Unknown",
);
#set output order - last field is for illustration purposes
my @field_order = qw ( Block Hard Soft Success Unknown this_field_missing );
my %count_of;
#iterate 'STDIN' or files specified to command line.
#So you can 'thisscript.pl /var/log/pmta/File_name-2017-03-23*'
while (<>) {
#split the line on commas
my ( $id, $em_addr, $ip, $timestamp, $msg_id, $code, $desc ) = split /,/;
#require msg_id contains '@email.domain.com'.
next unless $msg_id =~ m/\@email\.domain\.com/;
#split the status field on dash, extracting first word.
my ($status) = $code =~ m/^(\w+)-/;
#update the count - reference the 'keyword for' hash first,
#but insert 'raw' if it's something new.
$count_of{ $keyword_for{$status} // $status }++;
}
#print a header row (tab sep)
print join "\t", @field_order, "\n";
#print the rest of the values.
#map is so 'missing' fields get zeros, not 'undefined'.
print join "\t", ( map { $count_of{$_} // 0 } @field_order ), "\n";
鉴于您发布的小样本,此输出:
Block Hard Soft Success Unknown this_field_missing
2 2 4 0 1 0
答案 1 :(得分:1)
很难知道你想要什么结果,但我会在列表上下文中做反对,以便行已经分开,并用if
/ elsif
的链替换一个简单的哈希查找
此示例代码构建与您的哈希%report
相同的哈希值,并返回对它的引用。我不得不假设你似乎最有可能使用反叛。 Sobrique 是正确的,你的shell代码也应该在Perl中完成
my %map = (
blk => 'Block',
hrd => 'Soft',
sft => 'Block',
success => 'Success',
unk => 'Unknown',
);
my $cmd = q{cat /var/log/pmta/File_name-2017-03-23*|egrep 'email.domain.com'|cut -d, -f6|cut -d- -f1|sort|uniq -c};
sub get_stats {
my %report;
for ( `$cmd` ) {
my ($val, $type) = split;
$report{$map{$type}} = $val;
}
\%report;
}