views:

886

answers:

5

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");
  }
+3  A: 

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
u r great.... thanks alot dude...
lokesh
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
also - the bugs weren't even really in the opendir code, they were in print_file_names() !!
Alnitak
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
@Kent - thanks - using modules is great, but sometimes it's worth showing people how to do the underlying stuff properly
Alnitak
+1  A: 

Have you considered using

File::Find

Norbert Hartl
+3  A: 

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
+5  A: 

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
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
and this would find files (should they exist) at levels 1 and 3+ in the tree, contrary to his requirements...
Alnitak
@Alnitak: its easy to set mindepth and maxdepth for the search.
Kent Fredric
+2  A: 

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