KAKAK
KAKAK

Reputation: 899

Opening a directory and then scanning parent directories for matching files

    my $directory = shift @_;
    my @dh;
    my @files;

    opendir (my $dh, $directory) or die "Couldn't open dir '$directory' : $!";
    foreach my $file(readdir $dh) {
        if( -f $file =~ /\.htm$/){
                    push(@files,$file);
        }
        elsif(-d $file){
            push(@dh,$file);
            $dh = shift @dh;
        }

        closedir ($dh);
    }

I am trying to get my script to take in a path e.g. DATA/ (which is successful), however i want my script to scan through that directory and child directories and then if any file matches it will be stored in @files.

I am trying to use loop to scan through all subfolder e.g.

elsif(-d $file){
    push(@dh,$file);
    $dh = shift @dh;
}

In this statement $dh will get a new child directory name to scan through

However i am getting Segmentation fault

Upvotes: 1

Views: 114

Answers (1)

Borodin
Borodin

Reputation: 126722

There are a few problems here:

  • readdir returns the pseudo-directories . and .. and these should be ignored

  • readdir returns only the file name and not the full path to the file. So -f and -d will look in the current working directory for an entry with this name and probably won't find it

  • You are pushing directory names onto @dh but shifting directory handles $dh off it, which isn't going to work

  • The test -f $file =~ /\.htm$/ first applies the regex to the $file variable, and then uses the result of that (either 1 or "") as a parameter to -f. That isn't what you want at all

  • You jump to processing a new directory as soon as you find one, so the rest of the current directory isn't going to be processed

Things like this are normally done using File::Find or a recursive subroutine, but you can do something like this by eliminiating tail recursion. Note that it takes no account of directory links and can loop endlessly if it finds one. This is why it is best to use a module which has all these kinks ironed out.

use strict;
use warnings;

my @dh = @_;
my @files;

while (@dh) {

  my $directory = shift @dh;
  opendir my $dh, $directory or die "Couldn't open dir '$directory' : $!";

  while (readdir $dh) {
    next if /\A\.\.?\z/;
    my $node= "$directory/$_";

    if (-f $node and /\.html?$/i) {
      push @files, $node;
    }
    elsif (-d $node) {
      push @dh, $node;
    }
  }

}

Upvotes: 3

Related Questions