Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Greetings,

I was wondering if anyone could help me with this script Im trying to modify it to go through a directory and accurately output the total files and folders in the directory. Currently it is not giving accurate output because it is not searching throughout the entire directory. Thanks for any help that may be provided.

use strict; my $source = 'c:\directory'; my $folders = 0; my $files = 0; opendir (SOURCE, $source) or die "Cannot open the source folder for re +ading: $!\n"; my $file; while (defined($file = readdir(SOURCE))){ next if $file =~ /^\.{1,2}$/; my $full_name = "$source/$file"; if (-d $full_name){ #my $full_name = "$source/$file"; print qq($file); $folders++; print "\n"; process($full_name); }else{ print qq($full_name$file); $files++; print "\n"; } #next; } closedir (SOURCE); # Subroutine to process subdirectory and list content. sub process{ my ($path, $file, @files, $file_full_path); $path = shift; opendir (SUBDIR, $path) or die "Cannot open the subfolder for reading +: $!\n"; @files = sort grep {!/^\.{1,2}$/} readdir(SUBDIR); closedir (SUBDIR); for (@files){ $file_full_path = "$path/$_"; if (-d $_){ print qq($_); $folders++; print "\n"; process($_); }else { print qq($file_full_path); $files++; print "\n"; } } } print $folders; print "\n"; print $files;

Replies are listed 'Best First'.
Re: Count folder/files in directory
by Corion (Patriarch) on May 22, 2008 at 14:40 UTC
    use strict; use File::Find; my $source = 'c:/directory'; my $folders = 0; my $files = 0; $File::Find::dont_use_nlink = 1; # to fix a bug in File::Find File::Find::find( sub { if (-f $File::Find::name) { $files++ } elsif (-d $File::Find::name) { $folders++ }; }, $source ); print "Summary of $source\n"; print "Folders: $folders\n"; print "Files: $files\n";

    Also note how my code has a different background and a "download" link. This is because I wrapped it in <code>...</code> tags instead of using <pre> tags. PRE tags are kind of frowned upon here - see the Writeup Formatting Tips for further information.

      The OP tries to avoid counting the "." and ".." directories. Slip a "return if /^\.\.?\z/" in at the top of that anonymous sub, and I think that's it.

        For me, File::Find does call the callback with $File::Find::name neither equal to . nor ... I would consider such behaviour buggy, myself. Not that I consider File::Find to be free of bugs, but using it beats implementing the recursive routine and worrying about meta entries returned from readdir.

        File::Find::find will only process a "." directory if that is the directory you are starting at (it also processes "." if you start at ".."). If you don't want to count the directory(s) you start from, I think the easiest thing is just to subtract the number of starting directories from the directory count.
Re: Count folder/files in directory
by starbolin (Hermit) on May 22, 2008 at 16:29 UTC

    Inside your process subroutine you do not chance directories to the current directory you're reading nor do you use the full path of the file. Instead calling process on $_. Which begs the question why doesn't your script raise an error? It seems calling -d on the file is insufficient. I think it would be wise to test whether the file you are passing to process actually exists.

    One of the reasons for not catching this bug is that you have two nearly identical pieces of code. One in your main routine and the other your process subroutine. In this case you correctly called -d on the full path in you main code but not in your subroutine. It is errors like these why programmers eschew duplication. You have the process subroutine just call it initially instead of having a separate loop in you main code.

    Addendum: Your code seems to be confused about whether to use absolute or relative paths. You need to either:

  • Use relative paths; pushing the current path on a stack before calling process(), where you cd to the directory in question, then popping the saved directory upon return.
  • Use absolute paths where every call uses the full pathname. Process() being called with the full pathname and having to append the current directory to the pathname before calling itself.


    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
Re: Count folder/files in directory
by pc88mxer (Vicar) on May 22, 2008 at 17:00 UTC
    To elaborate on starbolin's comment, this code in process:
    $file_full_path = "$path/$_"; if (-d $_){ ... process($_);
    should use $file_full_path instead of $_:
    $file_full_path = "$path/$_"; if (-d $file_full_path){ ... process($file_full_path);
    And you should be able to kick off the whole traversal by calling process with your top-level directory:
    process($SOURCEDIR);
    Do this will eliminate the while loop at the beginning of your program which essentially duplicates the code you have in the process subroutine.