Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

recurse directories?

by Anonymous Monk
on Mar 12, 2000 at 02:04 UTC ( #5223=perlquestion: print w/replies, xml ) Need Help??

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

I'm writing a quick script which accepts files on the command-line (ARGV), and processes them. I want to check if a 'file' is actually directory ('-d $filename') - and if it's a directory, 'recurse' through that directory tree, and 'push' all the files in the directory onto the end of @ARGV. Some questions:<>

1) How would I recurse through the directory tree? How do I specify I only want '.mp3' files, or '.pl' files?

2) Is it safe to alter a scalar array in the middle of a 'foreach' loop? For example:

foreach $word (@WORDS) {
push(@WORDS, "hello") if $word eq "salutations";<> }

Replies are listed 'Best First'.
Re: recurse directories?
by Dominus (Parson) on Mar 12, 2000 at 03:30 UTC

    Yes, it's safe to do that, if what you want is what it happens to do.

    What you get with a foreach loop is that it does the first element in the array, then the second, then the third, and so on, until it runs out. If you change the elements as you go, it doesn't bother Perl; on the second iteration it gives you whatever the second element is at that moment, regardless of what it was when you started the loop. So for example:

    @a = (0,1,2); foreach $element (@a) { print $element, "\n"; $a[1] = 'foo'; }

    This prints out 0, foo, 2.

    Similarly, the loop ends only when Perl runs out of elements, and if you add elements in the middle of the loop, Perl will iterate over the new elements also, until it reaches the current end of the array. This code is an infinite loop:

    	@a = (1,2,3);
    	foreach $element (@a) {
    	  print $element, "\n";
    	  push @a, $element+3;
    	} 
    	  
    

    This prints 1 2 3 4 5 6 7... and so on forever, because it keeps extending the array, and Perl never gets to the end.

Re: recurse directories?
by Dominus (Parson) on Mar 12, 2000 at 03:25 UTC

    You want to use the File::Find module that comes with Perl. With File::Find you tell it what directory you want to search, and you supply a wanted subroutine that it invokes on every file that it finds. Inside of wanted, you get to do whetever you want, or you can just return immediately if you're not interested in a particular file. Your wanted might look like this:

    	sub wanted {
    	  return unless /\.mp3$/ || /\.pl$/;
    	  # do something with the .mp3 or .pl file
    	}
    
RE: recurse directories?
by Anonymous Monk on Mar 13, 2000 at 00:38 UTC
    You could also try the File::Recurse module on CPAN. Alternatively, if you're on a Linux system, a faster way of finding rare files is by backquoting, e.g.

    $location = `locate $name`;

    It's a hell of a lot faster than traversing the file tree manually as it uses an updated database of file locations rather than tromping all over the file system. (remember to update the file database with 'updatedb' as root before doing intensive searches.

    If on WinWhatever, ignore the above.

    From the File::Recurse docs:

    The File::Recurse module is designed for performing an operation on a tree of directories and files. The basic usage is simmilar to the find.pl library. Once one uses the File::Recurse module, you need only call the recurse function in order to perform recursive directory operations.

    The function takes two parameters a function reference and a directory. The function referenced by the first parameter should expect to take one parameter: the full path to the file currently being operated on. This function is called once for every file and directory under the directory named by the second parameter.

    For example:

    recurse(\&func, "/");

    would start at the top level of the filesystem and call "func" for every file and directory found (not including "/").

    Perl allows a second form of calling this function which can be useful for situations where you want to do something simple in the function. In these cases, you can define an anonymous function by using braces like so:

    recurse {print $_[0]} "/";

    This would print every file and directory in the filesystem. However, as an added convenience y

Re: recurse directories?
by Anonymous Monk on Mar 12, 2000 at 06:25 UTC
    Given: $extension='.mp3'; $dir="/path/to/some/directory"; opendir(DIR,$dir); Something like push(@ARGV, grep(/$extension$/,readdir()) OR push(@ARGV, grep(/$extension$/ && -f "$dir/$_", readdir(DIR)) Should do the trick.
Re: recurse directories?
by Benedictine Monk (Novice) on Mar 12, 2000 at 10:33 UTC
    You can roll your own recursive function to do this:
    sub parse_file ($) { #Do something with the file here. } sub traverse (@) { my @files = @_; local *DIRH; #If @files is empty, why bother? return unless @files; foreach my $file (@files) { #Skip unreadable files. next unless -r $file; if (-d $file) { #If it's a directory ... chomp(my $cwd = system('/bin/pwd')); #save current directory, .. +. chdir($file) || next; #change to the new director +y, ... opendir(DIRH,$file) || next; #open it... traverse((readdir(DIRH)); #and recursively parse it. closedir(DIRH); #Close the directory. chdir($cwd); #Change directories back to + where we were. } else { #If it's not a directory, then parse it. parse_file($file); #This is where the actual +work is done. } } } traverse(@ARGV);
RE: recurse directories?
by Anonymous Monk on Mar 13, 2000 at 00:37 UTC
    You could also try the File::Recurse module on CPAN. Alternatively, if you're on a Linux system, a faster way of finding rare files is by backquoting, e.g. $location = `locate $name`; It's a hell of a lot faster than traversing the file tree manually as it uses an updated database of file locations rather than tromping all over the file system. (remember to update the file database with 'updatedb' as root before doing intensive searches. If on WinWhatever, ignore the above. From the File::Recurse docs: The File::Recurse module is designed for performing an operation on a tree of directories and files. The basic usage is simmilar to the find.pl library. Once one uses the File::Recurse module, you need only call the recurse function in order to perform recursive directory operations. The function takes two parameters a function reference and a directory. The function referenced by the first parameter should expect to take one parameter: the full path to the file currently being operated on. This function is called once for every file and directory under the directory named by the second parameter. For example: recurse(\&func, "/"); would start at the top level of the filesystem and call "func" for every file and directory found (not including "/"). Perl allows a second form of calling this function which can be useful for situations where you want to do something simple in the function. In these cases, you can define an anonymous function by using braces like so: recurse {print $_[0]} "/"; This would print every file and directory in the filesystem. However, as an added convenience you can access the pathname in the variable $_. So the above could be r

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://5223]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (7)
As of 2022-01-20 14:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    In 2022, my preferred method to securely store passwords is:












    Results (56 votes). Check out past polls.

    Notices?