How can I recursively read out directories in Perl?
Solution 1
This should do the trick
use strict;
use warnings;
use File::Find qw(finddepth);
my @files;
finddepth(sub {
return if($_ eq '.' || $_ eq '..');
push @files, $File::Find::name;
}, '/my/dir/to/search');
Solution 2
You should always use strict and warnings to help you debug your code. Perl would have warned you for example that @files
is not declared. But the real problem with your function is that you declare a lexical variable @paths
on every recursive call to list_dirs
and don't push the return value back after the recursion step.
push @paths, list_dir($eachFile)
If you don't want to install additional modules, the following solution should probably help you:
use strict;
use warnings;
use File::Find qw(find);
sub list_dirs {
my @dirs = @_;
my @files;
find({ wanted => sub { push @files, $_ } , no_chdir => 1 }, @dirs);
return @files;
}
Solution 3
The answer by mdom explains how your initial attempt went astray. I would also suggest that you consider friendlier alternatives to File::Find
. CPAN has several options. Here's one.
use strict;
use warnings;
use File::Find::Rule;
my @paths = File::Find::Rule->in(@ARGV);
Also see here:
SO answer providing CPAN alternatives to
File::Find
.SO question on directory iterators.
And here is a rewrite of your recursive solution. Things to note: use strict
; use warnings
; and the use of a scoping block to create a static variable for the subroutine.
use strict;
use warnings;
print $_, "\n" for dir_listing(@ARGV);
{
my @paths;
sub dir_listing {
my ($root) = @_;
$root .= '/' unless $root =~ /\/$/;
for my $f (glob "$root*"){
push @paths, $f;
dir_listing($f) if -d $f;
}
return @paths;
}
}
Solution 4
I think you have problem in the following line in your code
for my $eachFile (glob($path.'*'))
You change the $path variable into $rootpath.
It will store the path correctly.
Solution 5
I use this script to remove hidden files (created by Mac OS X) from my USB Pendrive, where I usually use it to listen music in the car, and any file ending with ".mp3", even when it starts with "._", will be listed in the car audio list.
#!/bin/perl
use strict;
use warnings;
use File::Find qw(find);
sub list_dirs {
my @dirs = @_;
my @files;
find({ wanted => sub { push @files, $_ } , no_chdir => 1 }, @dirs);
return @files;
}
if ( ! @ARGV || !$ARGV[0] ) {
print "** Invalid dir!\n";
exit ;
}
if ( $ARGV[0] !~ /\/Volumes\/\w/s ) {
print "** Dir should be at /Volume/... > $ARGV[0]\n";
exit ;
}
my @paths = list_dirs($ARGV[0]) ;
foreach my $file (@paths) {
my ($filename) = ( $file =~ /([^\\\/]+)$/s ) ;
if ($filename =~ /^\._/s ) {
unlink $file ;
print "rm> $file\n" ;
}
}
Przemek
Updated on April 26, 2020Comments
-
Przemek about 4 years
I want to read out a directory recursively to print the data-structure in an HTML-Page with Template::Toolkit. But I'm hanging in how to save the Paths and Files in a form that can be read our easy.
My idea started like this
sub list_dirs{ my ($rootPath) = @_; my (@paths); $rootPath .= '/' if($rootPath !~ /\/$/); for my $eachFile (glob($path.'*')) { if(-d $eachFile) { push (@paths, $eachFile); &list_dirs($eachFile); } else { push (@files, $eachFile); } } return @paths; }
How could I solve this problem?
-
Przemek about 14 yearswith this routine I only get one result, and this is the directory I give the routine to start.
-
mdom about 14 yearsCan you show me the code you use to examine this? It's working just fine here. Maybe test it with "use Dumper; print Dumper list_dirs '/home'".
-
ed9w2in6 over 11 yearsso, glob actually doesn't support recursive listing of files, right?
-
Htbaa over 11 yearsthat's correct, it'll only get the files from the given directory.
-
mabalenk about 3 yearsCan you please explain what happens in the
find
command? It looks cryptic for a Perl novice.