Reputation: 197
Hi i want to read directories and sub-directories without knowing the directory name. Current directory is "D:/Temp". 'Temp' has sub-directories like 'A1','A2'. Again 'A1' has sub-directories like 'B1','B2'. Again 'B1' has sub-directories like 'C1','C2'. Perl script doesn't know these directories. So it has to first find directory and then read one file at a time in dir 'C1' once all files are read in 'C1' it should changes to dir 'C2'. I tried with below code here i don't want to read all files in array(@files) but need one file at time. In array @dir elements should be as fallows.
$dir[0] = "D:/Temp/A1/B1/C1"
$dir[1] = "D:/Temp/A1/B1/C2"
$dir[2] = "D:/Temp/A1/B2/C1"
Below is the code i tried.
use strict;
use File::Find::Rule;
use Data::Dumper;
my $dir = "D:/Temp";
my @dir = File::Find::Rule->directory->in($dir);
print Dumper (\@dir);
my $readDir = $dir[3];
opendir ( DIR, $readDir ) || die "Error in opening dir $readDir\n";
my @files = grep { !/^\.\.?$/ } readdir DIR;
print STDERR "files: @files \n\n";
for my $fil (@files) {
open (F, "<$fil");
read (F, my $data);
close (F);
print "$data";
}
Upvotes: 4
Views: 1655
Reputation: 19387
Your Goal: Find the absolute paths to those directories that do not themselves have child directories.
I'll call those directories of interest terminal directories. Here's the prototype for a function that I believe provides the convenience you are looking for. The function returns its result as a list.
my @list = find_terminal_directories($full_or_partial_path);
And here's an implementation of find_terminal_directories()
. Note that this implementation does not require the use of any global variables. Also note the use of a private helper function that is called recursively.
On my Windows 7 system, for the input directory C:/Perl/lib/Test, I get the output:
== List of Terminal Folders ==
c:/Perl/lib/Test/Builder/IO
c:/Perl/lib/Test/Builder/Tester
c:/Perl/lib/Test/Perl/Critic
== List of Files in each Terminal Folder: ==
c:/Perl/lib/Test/Builder/IO/Scalar.pm
c:/Perl/lib/Test/Builder/Tester/Color.pm
c:/Perl/lib/Test/Perl/Critic/Policy.pm
Implementation
#!/usr/bin/env perl
use strict;
use warnings;
use Cwd qw(abs_path getcwd);
my @dir_list = find_terminal_directories("C:/Perl/lib/Test");
print "== List of Terminal Directories ==\n";
print join("\n", @dir_list), "\n";
print "\n== List of Files in each Terminal Directory: ==\n";
for my $dir (@dir_list) {
for my $file (<"$dir/*">) {
print "$file\n";
open my $fh, '<', $file or die $!;
my $data = <$fh>; # slurp entire file contents into $data
close $fh;
# Now, do something with $data !
}
}
sub find_terminal_directories {
my $rootdir = shift;
my @wanted;
my $cwd = getcwd();
chdir $rootdir;
find_terminal_directories_helper(".", \@wanted);
chdir $cwd;
return @wanted;
}
sub find_terminal_directories_helper {
my ($dir, $wanted) = @_;
return if ! -d $dir;
opendir(my $dh, $dir) or die "open directory error!";
my $count = 0;
foreach my $child (readdir($dh)) {
my $abs_child = abs_path($child);
next if (! -d $child || $child eq "." || $child eq "..");
++$count;
chdir $child;
find_terminal_directories_helper($abs_child, $wanted); # recursion!
chdir "..";
}
push @$wanted, abs_path($dir) if ! $count; # no sub-directories found!
}
Upvotes: 2
Reputation: 6204
Perhaps the following will be helpful:
use strict;
use warnings;
use File::Find::Rule;
my $dir = "D:/Temp";
local $/;
my @dirs =
sort File::Find::Rule->exec( sub { File::Find::Rule->directory->in($_) == 1 }
)->directory->in($dir);
for my $dir (@dirs) {
for my $file (<"$dir/*">) {
open my $fh, '<', $file or die $!;
my $data = <$fh>;
close $fh;
print $data;
}
}
local $/;
lets us slurp the file's contents into a variable. Delete it if you only want to read the first line.sub
in the exec()
is used to pass only those dirs which don't contain a dirsort
is used to arrange those dirs in your wanted order<"$dir/*">
is used to get the files in each dirEdit: Have modified the code to find only 'terminal directories.' Thanks to DavidRR for this spec clarification.
Upvotes: 2
Reputation: 35198
use File::Find;
use strict;
use warnings;
my @dirs;
my %has_children;
find(sub {
if (-d) {
push @dirs, $File::Find::name;
$has_children{$File::Find::dir} = 1;
}
}, 'D:/Temp');
my @ends = grep {! $has_children{$_}} @dirs;
print "$_\n" for (@ends);
Upvotes: 3
Reputation: 4088
I would use File::Find
Sample script:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
my $dir = "/home/chris";
find(\&wanted, $dir);
sub wanted {
print "dir: $File::Find::dir\n";
print "file in dir: $_\n";
print "complete path to file: $File::Find::name\n";
}
OUTPUTS:
$ test.pl
dir: /home/chris/test_dir
file in dir: test_dir2
complete path to file: /home/chris/test_dir/test_dir2
dir: /home/chris/test_dir/test_dir2
file in dir: foo.txt
complete path to file: /home/chris/test_dir/test_dir2/foo.txt
...
Upvotes: 0
Reputation: 732
Using backticks, write subdirs and files to a file called filelist:
`ls -R $dir > filelist`
Upvotes: -1