User1611
User1611

Reputation: 1099

How do I read multiple directories and read the contents of subdirectories in Perl?

I have a folder and inside that I have many subfolders. In those subfolders I have many .html files to be read. I have written the following code to do that. It opens the parent folder and also the first subfolder and it prints only one .html file. It shows error:

NO SUCH FILE OR DIRECTORY

I dont want to change the entire code. Any modifications in the existing code will be good for me.

 use FileHandle;
 opendir PAR_DIR,"D:\\PERL\\perl_programes\\parent_directory";
 while (our $sub_folders = readdir(PAR_DIR))
 {
         next if(-d $sub_folders);

         opendir SUB_DIR,"D:\\PERL\\perl_programes\\parent_directory\\$sub_folders";
         while(our $file = readdir(SUB_DIR))
         {

       next if($file !~ m/\.html/i);
            print_file_names($file);    
         }
         close(FUNC_MODEL1);    
 }
 close(FUNC_MODEL);

  sub print_file_names()
  {
     my $fh1 = FileHandle->new("D:\\PERL\\perl_programes\\parent_directory\\$file")  
               or die "ERROR: $!"; #ERROR HERE 
     print("$file\n");
  }

Upvotes: 2

Views: 10738

Answers (6)

user5246166
user5246166

Reputation: 1

Here's one method which does not require to use File::Find:

First open the root directory, and store all the sub-folders' names in an array by using readdir;

Then, use foreach loop. For each sub-folder, open the new directory by linking the root directory and the folder's name. Still use readdir to store the file names in an array.

The last step is to write the codes for processing the files inside this foreach loop.

Special thanks to my teacher who has given me this idea :) It really worked well!

Upvotes: 0

Kent Fredric
Kent Fredric

Reputation: 57354

Your posted code looks way overcomplicated. Check out File::Find::Rule and you could do most of that heavy lifting in very little code.

use File::Find::Rule;

my $finder = File::Find::Rule->new()->name(qr/\.html?$/i)->start("D:/PERL/perl_programes/parent_directory");

while( my $file = $finder->match()  ){
   print "$file\n";
}

I mean isn't that sexy?!

A user commented that you may be wishing to use only Depth=2 entries.

use File::Find::Rule;

my $finder = File::Find::Rule->new()->name(qr/\.html?$/i)->mindepth(2)->maxdepth(2)->start("D:/PERL/perl_programes/parent_directory");

while( my $file = $finder->match()  ){
   print "$file\n";
}

Will Apply this restriction.

Upvotes: 6

Sinan Ünür
Sinan Ünür

Reputation: 118118

You are going to need to change the entire code to make it robust:

#!/usr/bin/perl

use strict;
use warnings;

use File::Find;

my $top = $ENV{TEMP};

find( { wanted => \&wanted, no_chdir=> 1 }, $top );

sub wanted {
    return unless -f and /\.html$/i;
    print $_, "\n";
}

__END__

Upvotes: 3

Alnitak
Alnitak

Reputation: 339776

You're not extracting the supplied $file parameter in the print_file_names() function.

It should be:

sub print_file_names()
{
    my $file = shift;
    ...
}

Your -d test in the outer loop looks wrong too, BTW. You're saying next if -d ... which means that it'll skip the inner loop for directories, which appears to be the complete opposite of what you require. The only reason it's working at all is because you're testing $file which is only the filename relative to the path, and not the full path name.

Note also:

  1. Perl on Windows copes fine with / as a path separator
  2. Set your parent directory once, and then derive other paths from that
  3. Use opendir($scalar, $path) instead of opendir(DIR, $path)

nb: untested code follows:

use strict;
use warnings;
use FileHandle;

my $parent = "D:/PERL/perl_programes/parent_directory";

my ($par_dir, $sub_dir);
opendir($par_dir, $parent);
while (my $sub_folders = readdir($par_dir)) {
    next if ($sub_folders =~ /^..?$/);  # skip . and ..
    my $path = $parent . '/' . $sub_folders;
    next unless (-d $path);   # skip anything that isn't a directory

    opendir($sub_dir, $path);
    while (my $file = readdir($sub_dir)) {
        next unless $file =~ /\.html?$/i;
        my $full_path = $path . '/' . $file;
        print_file_names($full_path);    
    }
    closedir($sub_dir);
}
closedir($par_dir);

sub print_file_names()
{
     my $file = shift;
     my $fh1 = FileHandle->new($file) 
           or die "ERROR: $!"; #ERROR HERE 
     print("$file\n");
 }

Upvotes: 4

Zenshai
Zenshai

Reputation: 10707

Please start putting:

use strict;
use warnings;

at the top of all your scripts, it will help you avoid problems like this and make your code much more readable.

You can read more about it here: Perlmonks

Upvotes: 3

Norbert Hartl
Norbert Hartl

Reputation: 10841

Have you considered using

File::Find

Upvotes: 1

Related Questions