Perl Regex - 从邮件日志中提取 ipv4

Perl Regex - extracting ipv4 from maillog

我正在 perl/mysql/iptables 中开发类似 fail2ban 的分布式系统。

正在从 /var/log/messages 中提取 ipv4 地址,但现在 我想在汤里加/var/log/maillog

我有一个 perl 正则表达式:[1]

/ (?:25[012345]|2[0-4]\d|1?\d\d?)\.
  (?:25[012345]|2[0-4]\d|1?\d\d?)\.
  (?:25[012345]|2[0-4]\d|1?\d\d?)\.
  (?:25[012345]|2[0-4]\d|1?\d\d?) /x

邮件日志中的一行:

v817YjcU016645: 194.102.60.190.host.ifxnetworks.com [190.60.102.194] did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA

这里的正则表达式同时匹配 194.102.60.190.host.ifxnetworks.com 和 [190.60.102.194]

在我的代码中我有($IP 是上面的正则表达式):

if ($line =~ m/($IP)/)
{
    my ($ip) = ;

这里找到第一个匹配的类似 ip 的字符串 194.102.60.190.host.ifxnetworks.com

那么,如何让正则表达式忽略以 .

结尾的 ipv4

[1] 为了便于阅读,Perl 支持 /x option

一般来说,正则表达式匹配现有字符序列中想要的模式,如果存在不需要的东西,则不匹配总是有点困难。

您可以匹配后跟非点 ([^.]):

的 IP 地址[1]
(?:\d{1,3}\.){3}\d{1,3}[^.]

和行尾的 IP 地址 ($):

(?:\d{1,3}\.){3}\d{1,3}$

您可以在非捕获组中通过更改 (|) 将这两种模式结合起来 ((?:...)):

(?:\d{1,3}\.){3}\d{1,3}(?:[^.]|$)

一个类似的问题可能是您的下一个任务可能是排除前面有一个点的 IP 地址,另一个问题是它也会匹配 1.2.3.4.5 中的 2.3.4.5,这导致返回我的介绍性陈述...

我认为您尝试匹配的 IP 地址最好通过同时检查周围字符的东西找到。具体一点。对于开发阶段,尝试通过将它们匹配到 "garbage patterns" 来检查不匹配的行。在问题中显示的情况下(空格和括号是可接受的环境),我建议使用

(?:[ \[]|^)((?:\d{1,3}\.){3}\d{1,3})(?:[ \]]|$)

[1] 我这里使用了一个简化的正则表达式,它也匹配 333.333.333.333000.000.000.000,当然可以改进限制匹配到有效的 IP 地址,但解决方案是 abundant.

如果这是唯一的问题,请尝试 negative lookahead

my ($ip) = $line =~ /($IP)(?![.\d])/;

适用于显示的数据。

前瞻 [.\d] 中的字符 class 是必需的,因为 $IP 正则表达式中的最后一项通过 \d? 允许可变数量的数字.因此,单独使用 (?!\.) 引擎可以匹配的数字比现有数字少一位,然后剩余的数字满足非 . 限制。

因此我们需要禁止 . 和模式后面的数字。


一个完整的程序

use warnings;
use strict;

my $t = 'a 194.102.60.190.host.ifxnetworks.com [190.60.102.194] b';

my $n = qr/(?:25[012345]|2[0-4]\d|1?\d\d?)/;

my $IP = qr/$n\.$n\.$n\.$n/;

my @m = $t =~ /($IP)(?![.\d])/g;

print "@m\n";

打印190.60.102.194


考虑子字符串 90.host。它的模式 /\d\d?(?!\.)/ 工作方式如下。

第一个 \d 匹配 9。但是下一个 \d? 是可选的(非贪婪的)并且它 不匹配 如果模式的其余部分可以匹配。实际上,(?!\.) 认为后面的 0 不是 .,因此我们匹配 9 并且 0 满足 (?!\.)。整个模式(错误地)匹配

perl -wE'$_ = q(90.host); @m = /(\d)(\d?)(?!\.)(.)/; say for @m'

打印

9

0

中间的捕获组什么也没捕获,下一个字符 (.)0.

现在考虑相同子字符串的模式 /\d\d?(?![.\d])/(?![.\d]) 要求后面的不是 . 也不是 数字。因此,可选的 \d? 被迫匹配下一个数字 0。由于下一个字符 一个 . 模式失败。

在上面的单行测试中使用 (?![.\d]) 而不是 (?!\.) 没有打印任何内容,因为模式根本不匹配。 (在某些 shell 中,您可能必须转义 !,因此 (?\![.\d]),或使用脚本。)

引擎可能不会像描述的那样完全,这更像是对其操作的粗略描述。