1

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");
  }
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
User1611
  • 1,081
  • 4
  • 18
  • 27
  • tip: prefer using "my" to "our", and run perl with the -w option (and "use strict" if at all possible). that would have helped show up this error earlier: using "my" but without strict, it would have warned you that "$file" was undefined, and with strict it would have died before running saying that "$file" was undeclared. – araqnid May 18 '09 at 18:15

6 Answers6

6

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.

Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
  • 1
    name("*.html") doesn't match the same files that his regex did. Fortunately, name() accepts regexes as well as globs. I've fixed the condition to match. – cjm May 18 '09 at 18:32
  • and this would find files (should they exist) at levels 1 and 3+ in the tree, contrary to his requirements... – Alnitak May 18 '09 at 19:24
  • @Alnitak: its easy to set mindepth and maxdepth for the search. – Kent Fredric May 18 '09 at 21:29
4

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");
 }
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 1
    For all those who've downvoted me for not using File::Find - a) I was showing him how to use opendir etc properly, and b) he only specified two levels of directories, with the files all being at level 2. – Alnitak May 18 '09 at 19:20
  • also - the bugs weren't even really in the opendir code, they were in print_file_names() !! – Alnitak May 18 '09 at 19:22
  • I upvoted you for the correct open() stuff, but also added the measly 2 function calls to my own answer to make it support depth limitation. ;) . If there's something File::Find::Rule can't do, I'm yet to see it. – Kent Fredric May 18 '09 at 21:33
  • @Kent - thanks - using modules is great, but sometimes it's worth showing people how to do the underlying stuff properly – Alnitak May 18 '09 at 22:56
3

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

Zenshai
  • 10,307
  • 2
  • 19
  • 18
3

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__
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
1

Have you considered using

File::Find

Norbert Hartl
  • 10,481
  • 5
  • 36
  • 46
0

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!