Richard
Richard

Reputation: 3

Copy the IP Addresses in several lists into a single list

I'm trying to create a single of list of IP addresses from a several different lists that are located in the same directory. Each list has other text and words in addition to the IP addresses. I have the following code:

#!/usr/bin/perl
use strict;
use warnings;

my $dir = "/home/RHart1/blacklists";

opendir(my $dh, $dir) or die "cannot open directory";
my @lists = grep(/(((\w+[-_]\w+){1,5})\.\w+)$/,readdir($dh));

foreach my $file (@lists) {
    open (LIST, "$file") or die $!;
    while (<LIST>) {
         open (OUTPUTLIST, '>>badguys.txt');
         if (/^(([0-9]{1,3}\.){3}[0-9]{1,3}($|\/([0-9]{1,2})))$/) {
               print OUTPUTLIST "$1\n";
         }
    close $file;
   }
}

close OUTPUTLIST;
closedir $dh;

However, I'm running to a problem at line 11. where it says:

No such file or directory at ./testperl.pl line 11.

What am I doing wrong here?

Thanks.

Upvotes: 0

Views: 52

Answers (1)

Miller
Miller

Reputation: 35208

From the documentation for readdir:

If you're planning to filetest the return values out of a readdir, you'd better prepend the directory in question. Otherwise, because we didn't chdir there, it would have been testing the wrong file.

Therefore, when opening a file, be sure to include the path information.

open (LIST, "$dir/$file") or die $!;

Alternatively, you could chdir to the directory that you're reading.

Using Modern Perl style

I'd also advise some general modernizing of your code.

  • Use autodie to get better, automatic error messages for file operations
  • Use lexical file handles with the 3 parameter form of open

These two and a couple minor fixes would result in the following:

#!/usr/bin/perl
use strict;
use warnings;
use autodie;

my $dir = "/home/RHart1/blacklists";

open my $outfh, '>>', 'badguys.txt';

opendir my $dh, "$dir";
while ( my $file = readdir $dh ) {
    next unless $file =~ /(\w+[-_]\w+){1,5}\.\w+$/;

    open my $fh, '<', "$dir/$file";
    while (<$fh>) {
        if (/^(([0-9]{1,3}\.){3}[0-9]{1,3}($|\/([0-9]{1,2})))$/) {
            print $outfh "$1\n";
        }
    }
}

Using Path::Class for Cross-platform path and file manipulation

Perl core comes with File::Spec for cross-platform file manipulation. It works but it's awkward. As an alternative though, there is Path::Class which provides a much cleaner interface to the core library in the backend.

It will require an install, but the following is your script done using this cpan module:

use strict;
use warnings;
use autodie;

use Path::Class;

my $dir = dir('', 'home', 'RHart1', 'blacklists');

open my $outfh, '>>', 'badguys.txt';

while (my $file = $dir->next) {
    next unless $file->basename =~ /(\w+[-_]\w+){1,5}\.\w+$/;

    my $fh = $file->openr();
    while (<$fh>) {
        if (/^(([0-9]{1,3}\.){3}[0-9]{1,3}($|\/([0-9]{1,2})))$/) {
            print $outfh "$1\n";
        }
    }
}

Upvotes: 3

Related Questions