Perl正则表达式中的非捕获组,组内包含交替捕获。

4

我正在尝试解析几个邮件日志,其中的中继可能有以下三种格式。

Oct 24 03:49:10 mxout/mxout/1.1.1.1 sendmail[4642]: x9NA4Wbp011336: to=<email@company.com>, delay=1+00:44:37, xdelay=00:00:00, mailer=esmtp, pri=459, relay=mail-company.com. [0.0.0.0], tls=no, dsn=4.0.0, stat=Deferred: Connection reset by mail-company.com
Oct 24 03:49:10 mxout/mxout/1.1.1.1 sendmail[4642]: x9NA4Wbp011336: to=<email@company.com>, delay=1+00:44:37, xdelay=00:00:00, mailer=esmtp, pri=459, relay=[0.0.0.0], tls=no, dsn=4.0.0, stat=Deferred: Connection reset by mail-company.com
Oct 24 03:49:10 mxout/mxout/1.1.1.1 sendmail[4642]: x9NA4Wbp011336: to=<email@company.com>, delay=1+00:44:37, xdelay=00:00:00, mailer=esmtp, pri=459, relay=mail-company.com., tls=no, dsn=4.0.0, stat=Deferred: Connection reset by mail-company.com

使用以下代码:

代码如下:

my $topat    = '^(\w{3})\s{1,2}(\d{1,2}) (\d{2}:\d{2}:\d{2}).+ sendmail\[\d.+\]: (\w+): to=<(\S+)>(?:,|, \[more\],) delay.+, relay=(?:(?:\S+ )?\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]|(\S+)\.), .+, stat=(.+)';

foreach my $line(@i) {
  if($line =~ /$topat/){
    my ($month, $day, $time, $id, $addy, $relay, $stat) = ($line =~ m/$topat/);
     print $line;
     print "$addy $relay $stat\n";
  }
}

我遇到了以下错误:
Oct 24 03:49:10 mxout/mxout/1.1.1.1 sendmail[4642]: x9NA4Wbp011336: to=<email@company.com>, delay=1+00:44:37, xdelay=00:00:00, mailer=esmtp, pri=459, relay=mail-company.com. [0.0.0.0], tls=no, dsn=4.0.0, stat=Deferred: Connection reset by mail-company.com
Use of uninitialized value $stat in concatenation (.) or string at ./reg_test line 26.
email@company.com 0.0.0.0 

Oct 24 03:49:10 mxout/mxout/1.1.1.1 sendmail[4642]: x9NA4Wbp011336: to=<email@company.com>, delay=1+00:44:37, xdelay=00:00:00, mailer=esmtp, pri=459, relay=[0.0.0.0], tls=no, dsn=4.0.0, stat=Deferred: Connection reset by mail-company.com
Use of uninitialized value $stat in concatenation (.) or string at ./reg_test line 26.
email@company.com 0.0.0.0 

Oct 24 03:49:10 mxout/mxout/1.1.1.1 sendmail[4642]: x9NA4Wbp011336: to=<email@company.com>, delay=1+00:44:37, xdelay=00:00:00, mailer=esmtp, pri=459, relay=mail-company.com., tls=no, dsn=4.0.0, stat=Deferred: Connection reset by mail-company.com
Use of uninitialized value $relay in concatenation (.) or string at ./reg_test line 26.
email@company.com  mail-company.com

在前两种情况下,它会正确获取地址和中继,但不包括状态。在第三种情况下,它可以获取地址和中继,但认为 $relay 为空,$stat 是中继。 我尝试了许多不同的配置和组合,但似乎找不到正确的解决方案。任何指针都将不胜感激。

在第一行中,“stat”(最后一组)与“Deferred:Connection reset by mail-company.com”匹配,这不是预期的吗?我也没有看到第三行有任何特定的问题,请参见您的正则表达式演示 - Wiktor Stribiżew
在测试器中它确实匹配正确,但是当我尝试使用上述代码打印它,或者如果我尝试打印"$1 $2 $3 $4 $5 $6 $7\n",我会遇到与上面相同的错误和问题。 - tleif
1个回答

3

relay 字段中,您有两个选择:

relay=(?:(?:\S+ )?\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]|(\S+)\.)
                    ^    ----      $6         ----     ^  | ^$7^ 

如果不符合第一种模式但匹配第二种模式,继电器最终将结束在$7$stat上。由于需要$8而非$7,$stat永远不会被正确填充。
您可以使用分支重置模式,使用相同的捕获编号来处理所有备选项:
(?|(?:\S+ )?\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]|(\S+)\.)
  ^

或者,使用原始正则表达式并填充两个变量:

    my ($month, $day, $time, $id, $addy, $relay, $relay_alt, $stat) = $line =~ m/$topat/;
    $relay //= $relay_alt;

太棒了,将 : 替换为 | 完美地解决了问题。谢谢。 - tleif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接