When asked to match an IP address, many will write
/\d+\.\d+\.\d+\.\d+/
but this will give false positives. In Jeffrey Friedl's Mastering Regular Expressions, the author gives a pattern for matching IP addresses that's much more careful. The code below borrows from Friedl to force not an arbitrary run of digits but on the range from 0 to 255, requires that an address begin and end at word boundaries (\b
), and disallows the address 0.0.0.0.
With no arguments, the code below defaults to the current directory for the beginning of its search. To search all files, supply the root directory as an argument. Opening each path that find
outputs, we then search each line for an IP address and print all hits along with their respective paths.
Note how the code uses local
to transparently switch back and forth between the NUL character and newline for the record separator $/
. This is necessary because the find
's -print0
action separates filenames with '\0'
, but '\n'
is the line terminator. With -T
, we search text files only.
#! /usr/bin/perl
use warnings;
no warnings 'exec';
use strict;
my $octet = qr/[01]?\d\d?|2[0-4]\d|25[0-5]/;
my $ip = qr/ \b
(?!0+\.0+\.0+\.0+\b)
$octet(?:\.$octet){3}
\b
/x;
@ARGV = (".") unless @ARGV;
open my $find, "-|", "find", @ARGV, "-type", "f", "-print0"
or die "$0: failed to start find: $!\n";
$/ = "\0";
while (defined(my $path = <$find>)) {
chomp $path;
next unless -T $path;
if (open my $fh, "<", $path) {
local $/ = "\n";
while (<$fh>) {
print "$path: $_" if /$ip/;
}
close $fh;
}
else {
warn "$0: open $path: $!\n";
}
}