# SUBROUTINE: get_sub_dirs
Call this function when you want to return an array of folders which exists within a certain path.
All you have to do is pass this function the path you want to scan for sub directories. If any subdirectories are found, it will return an array of the directory names. This array will be unsorted; so if you want it sorted, just do that after you have the array returned. If no sub-dirs are found in the path you specified, it will return a '0' (zero). This can be easily customized to return a string instead.

EXAMPLE: Specify the path you want $path = "C:\\Bible\\Ephesians\\Chapter2\\Verses8_9";

Call the function @dir_listing = &get_sub_dirs($path);

NOTE: This function will change the program's 'present working directory' when it executes, so you may want to change it back to whatever 'pwd' you need after its done executing.
ALSO NOTE: This function will DIE if you pass it an invalid path.
sub get_sub_dirs { #Made by: cynix #Local Variable for Path Passed to This Sub local($tmp_path) = @_; #<------ OPEN THE REQUESTED DIR PATH AND GET A FILE LISTING ------ opendir(TMP_DIR, $tmp_path) || die "Cannot Open $tmp_path"; local(@tmp_dir_listing) = readdir(TMP_DIR); closedir(TMP_DIR); #----------------------------------------------------------------- #NOTE THE CODE: local($nmbr_items) = @some_array; #DOESNT WORK SO ALWAYS DECLARE IT FIRST. local($nmbr_items); $nmbr_items = @tmp_dir_listing; #Variable to Hold the "Clean" Directory Listing local(@tmp_clean_listing); #Variable to Hold Index Count for "Clean" Listing local($nmbr_clean_items) = 0; #INIT THE "Clean" DIR listing to "empty" #YOU CAN MODIFY THIS VARIABLE TO RETURN WHATEVER YOU #WANT IN CASES WHERE THERE WAS NO DIRECTORY FOUND $tmp_clean_listing[0] = 0; #Variable to Hold a Directory Index String local($item); #Change the Working Directory, so PERL can do File Test Operations chdir $tmp_path; #Scan Through Array, Excluding the Notorious '.' & '..' for ($i=2;$i<$nmbr_items;$i++) { $item = $tmp_dir_listing[$i]; #Test $item and See if it Really is a Dir if (-d $item) { #ITEM IS A DIR -> SO EXTRACT IT TO THE CLEAN ARRAY $tmp_clean_listing[$nmbr_clean_items] = $tmp_dir_listing[$i]; $nmbr_clean_items = $nmbr_clean_items + 1; } else { #DO NOTHING::ITEM IS NOT A DIR } } return(@tmp_clean_listing); }

Replies are listed 'Best First'.
Re: Return an Array of Sub-Dir Names
by wog (Curate) on Sep 09, 2001 at 04:36 UTC
    This subroutine appears to be written for perl 4 (update: which is over 7 years out of date.) . This is evidenced by the use of local, not my which wasn't in perl 4, and the lack of grep or push which also weren't in perl 4 (strikethrough is update as of 27 Jan 2002, thanks to grinder). In perl 5, my is strongly preferred to local in most circumstances (see also perlsub under the heading "Temporary Values via local()"). Also grep is much easier to use then a for loop for "filtering" a list.

    This subroutine also relies on . and .. being returned from readdir first. This is not a portable assumption.

    Here is the subroutine re-written not to have those problems:

    sub get_sub_dirs { use File::Spec; # so we don't have problems with # platforms which use odd path # seperators. my($path) = @_; my @dirs; opendir(DIR, $path) or die "Cannot open $path: $!\n"; @dirs = grep { # not . or .. !/\A\.{2}\z/ # XXX: Won't work on OSes where # normal directories can be called # '.' or '..' (e.g. MacOS)? and # is a directory -d File::Spec->catfile($path, $_); # (using catfile no chdir is needed; # we just get the full pathname of # the file in question.) } readdir DIR; closedir DIR; return @dirs; }
Re: Return an Array of Sub-Dir Names
by merlyn (Sage) on Sep 09, 2001 at 22:00 UTC
      File::Find will get us an entire directory tree ;-) ... so you should insert some 'prune' there :)

      ...wouldn't globing be good enough for this simple job? =)

      sub subdirs { my $dir = shift; my @subd = (); for(<$dir/*>) { next unless -d; s".+/""; # lame 'basename' surogat :) push @subd, $_; } @subd; }
      .. IAE, it is indeed too much work for this ;)

      --
      AltBlue.

Re: Return an Array of Sub-Dir Names
by blakem (Monsignor) on Sep 09, 2001 at 04:51 UTC
    Nice idea, I can see how this would come in handy. However, there are a few ways in which this code could be cleaned up a bit.

    First, you are using local in places where my would be better. See The difference between my and local for a more indepth discussion... I'd recommend replacing every instance of local with my in the code above.

    Second, you have the comment:

    #NOTE THE CODE: local($nmbr_items) = @some_array; #DOESNT WORK SO ALWAYS DECLARE IT FIRST.
    Which is a *great* comment, because it lets me know what you're thinking and helps me understand why the code is written the way it is. Lets assume you've already followed my advice above and replaced local($nmbr_items) with my($nmbr_items) You'll still have the same issue mentioned in the comment, namely that wrapping  my( ... ) around a variable can alter things somewhat subtly. When used in this manner, my will impose a list context onto the right hand side of the assignment. The return value resulting from evaluating an array in scalar vs. list context is different, so you get different results. The solution is to drop the parens and modify the code to read my $nmbr_items = ... This will behave how you want it to.

    Finally, the structure of the code is:

    while (read directory) { add to temp array; } examine array to set up loop vars for next loop while (loopvars tell us were still in the loop) { generate return array }
    Why not combine the directory reading loop and the return value generating loop.. This elimiates will greatly simplify the code, down to something such as:
    my @returnvalue; while(read directory) { skip some bad entries ('.' , '..', and non-dirs) add value to @returnvalue } return @returnvalue;

    -Blake