Failing File Tests?

J

Jay Sun Ex

I am working on a recursive program that prints out all directories
and files recursively starting from whatever directory is passed to
ARGV[0]. This version of my program is meant for use on windows 2000.
The problem is my program does not capture directories using the if
(-d $blah... file test. Below is my code. Any and all help would be
greatly appreciated.

$root = $ARGV[0];
&recurse($root);
sub recurse {
opendir DIR, $_[0];
foreach $file (readdir DIR) {
if (($file eq ".") or ($file eq "..")) {
#Do nothing
} elsif (-d $file) {
print "\n$file";
$folder_path = $_[0] . "\\" . $file;
push @directories, $folder_path;
} else {
print "\n$file is not a directory";
}
}
close DIR;
foreach $newpath (@directories) {
shift @directories;
&recurse($newpath);
}
}
 
P

Paul Lalli

I am working on a recursive program that prints out all directories
and files recursively starting from whatever directory is passed to
ARGV[0]. This version of my program is meant for use on windows 2000.
The problem is my program does not capture directories using the if
(-d $blah... file test. Below is my code. Any and all help would be
greatly appreciated.

$root = $ARGV[0];
&recurse($root);
sub recurse {
opendir DIR, $_[0];
foreach $file (readdir DIR) {
if (($file eq ".") or ($file eq "..")) {
#Do nothing
} elsif (-d $file) {
print "\n$file";
$folder_path = $_[0] . "\\" . $file;
push @directories, $folder_path;
} else {
print "\n$file is not a directory";
}
}
close DIR;
foreach $newpath (@directories) {
shift @directories;
&recurse($newpath);
}
}


Are you doing this as a learning experience? If so, the answer is that
readdir returns the filename without the path. That is, -d is looking for
a file named $file within the current working directory, not the directory
in which it was found.

If you're trying to do this for an actual practical purpose, stop wasting
effort. It's already been done, in the standard module File::Find.

Paul Lalli
 
R

Randal L. Schwartz

Paul> If you're trying to do this for an actual practical purpose, stop wasting
Paul> effort. It's already been done, in the standard module File::Find.

Or the bolt-on layers from the CPAN, the older File::Find::Rule,
and my own File::Finder.
 
J

Jay Sun Ex

Paul Lalli said:
Are you doing this as a learning experience? If so, the answer is that
readdir returns the filename without the path. That is, -d is looking for
a file named $file within the current working directory, not the directory
in which it was found.

If you're trying to do this for an actual practical purpose, stop wasting
effort. It's already been done, in the standard module File::Find.

Paul Lalli

As I am still very much a beginner and doing this for learning
purposes, what should I use since readdir is no longer my friend?

Also, I have zero experience with modules at this point. I am hesitant
to use them for two reasons, I like the challenge of solving problems
with my own code, and second, I want my programs to be able to run on
various systems that may or may not have modules installed. If I use a
module and want my program to run on some other machine, does that
machine need the module installed or does my script just know what to
do?

Thanks for all the help thus far!

-Jason X-
 
P

Paul Lalli

As I am still very much a beginner and doing this for learning
purposes, what should I use since readdir is no longer my friend?

readdir is your friend. You're just not using -d correctly. Once you've
gotten the name of the file that exists in a given directory, you can't
just use the filetest on the filename. You have to use it on the whole
path. An example:

openddir $dir, $dirname or die "Cannot open directory $dirname: $!";
while ($filename = readdir ($dir)){
if (-d "$dirname/$filename") {
#whatever you want to do with directories
}
}
Also, I have zero experience with modules at this point. I am hesitant
to use them for two reasons, I like the challenge of solving problems
with my own code,

That's an okay reason - but remember one of the three great virtues of
programming - laziness. That means reusing existing code rather than
reinventing it.
and second, I want my programs to be able to run on
various systems that may or may not have modules installed.

That's not, at least in this case.
If I use a
module and want my program to run on some other machine, does that
machine need the module installed or does my script just know what to
do?

File::Find is a *standard* module. It comes with Perl. If you have Perl,
you have this module.

The other two that Mr. Schwartz mentioned, File::Finder and
File::Find::Rule, are non-standard. A script which uses these modules on
an installation which does not contain these modules will fail, with an
error similar to "Cannot find File/Finder.pm in @INC. @INC
contains....". This is trivial to fix, however. You simply install the
module, by using the ever-handly CPAN module:

perl -MCPAN -e'install File::Finder'
Thanks for all the help thus far!

For more information on the standard File::Find module, run this in your
shell:

perldoc File::Find

For more information on the other two, visit search.cpan.org, and search
for those two modules' names.

Paul Lalli
 
D

David K. Wall

As I am still very much a beginner and doing this for learning
purposes, what should I use since readdir is no longer my friend?

readdir() is still your friend, but you'll have to keep track of the
path yourself.
Also, I have zero experience with modules at this point. I am
hesitant to use them for two reasons, I like the challenge of
solving problems with my own code, and second, I want my programs
to be able to run on various systems that may or may not have
modules installed. If I use a module and want my program to run on
some other machine, does that machine need the module installed or
does my script just know what to do?

It depends on which modules you use. File::Find is a standard module,
meaning that it comes with every Perl installation. If it's not
there, then the install was screwed up or someone removed it for some
strange reason.

If you're trying to get real work done, modules are a godsend.
Instead of trying to solve every hairy problem yourself, it's often
the case that someone else has already solved it and made reusable
code available. The CPAN (Comprehensive Perl Archive Network) is
sometimes envied by users of other languages who otherwise don't care
for Perl.
 
A

Andrew Palmer

Jay Sun Ex said:
Paul Lalli <[email protected]> wrote in message

As I am still very much a beginner and doing this for learning
purposes, what should I use since readdir is no longer my friend?

Also, I have zero experience with modules at this point. I am hesitant
to use them for two reasons, I like the challenge of solving problems
with my own code, and second, I want my programs to be able to run on
various systems that may or may not have modules installed. If I use a
module and want my program to run on some other machine, does that
machine need the module installed or does my script just know what to
do?

Yes, the module would have to be installed, but so would perl, of course.
File::Find is a standard module. If perl5 is installed, it should already be
there.

Also, installing a module from CPAN is not that big a deal. Most people will
do it if they have to to use a particular script.
 
B

Ben Morrow

Quoth (e-mail address removed) (Jay Sun Ex):
As I am still very much a beginner and doing this for learning
purposes, what should I use since readdir is no longer my friend?

readdir is still your friend: you just have either chdir to the
directory you are reading or prepend the name of the directory to the
filename readdir gave you: use the standard module File::Spec for this,
to do it in a protable fashion.
Also, I have zero experience with modules at this point. I am hesitant
to use them for two reasons, I like the challenge of solving problems
with my own code,

This is good to do as a learning exercise; for production code, modules
are in general safer, more stable and often faster than what you would
write. Remember they have been peer-reviewed by people far better at
Perl than you (or me).
and second, I want my programs to be able to run on
various systems that may or may not have modules installed. If I use a
module and want my program to run on some other machine, does that
machine need the module installed or does my script just know what to
do?

If you are using 'core' modules (those that come with Perl) then they
should be on any machine with perl. If you are using others, then you
will need to ensure they are present to run the script; but since you
need Perl anyway, an extra module or two is not a big problem. See
PAR.pm (a module...) for a way to package up your program and all (or
all non-core) modules it needs, and possibly a perl interpreter as well
(creating a completely stand-alone executable).

Ben
 
J

Jay Sun Ex

I figured out the problem. Below is the working code. I also figured
out the answer to the module question. Thanks for all the help!

&recurse($ARGV[0]);
sub recurse {
opendir DIR, $_[0];
foreach $file (readdir DIR) {
if (($file eq ".") or ($file eq "..")) {
#Do nothing
} elsif (-d ($_[0] . "\\" . $file)) {
print "\n$file";
$folder_path = $_[0] . "\\" . $file;
push @directories, $folder_path;
} else {
print "\n$file is not a directory";
}
}
close DIR;
foreach $newpath (@directories) {
shift @directories;
&recurse($newpath);
}
}

Thanks again everyone!
 
A

A. Sinan Unur

(e-mail address removed) (Jay Sun Ex) wrote in

use strict;
use warnings;
&recurse($ARGV[0]);

You should only call subroutines with & if you need the specific behavior.
Read perldoc perlsub for more info.
sub recurse {
opendir DIR, $_[0];

opendir might fail.
foreach $file (readdir DIR) {
if (($file eq ".") or ($file eq "..")) {
#Do nothing

next if ($file eq '.' or $file eq '..');
} elsif (-d ($_[0] . "\\" . $file)) {

I would really recommend using File::Spec->catfile for this purpose. It
helps with one aspect of portability and enhances readibility as well as
helps one avoid typos.
print "\n$file";
$folder_path = $_[0] . "\\" . $file;

Why do the same thing twice?
push @directories, $folder_path;
} else {
print "\n$file is not a directory";
}
}
close DIR;
foreach $newpath (@directories) {
shift @directories;
&recurse($newpath);
}
}

May I recommend the following:

#! perl

use strict;
use warnings;

use File::Spec::Functions qw(catfile);

$| = 1;

my $top = $ARGV[0];
$top = '.' unless defined $top;

recurse($top);

sub recurse {
my $dir = shift;
my @list;

{
opendir my $DIR, $dir or die "Cannot open $dir: $!";
@list = readdir $DIR;
closedir $DIR or die "Error closing $dir: $!";
}

@list = grep { $_ ne '.' and $_ ne '..' } @list;
@list = map { catfile $dir, $_ } @list;

my @todo;

for (@list) {
unless( -d ) {
print "$_ is not a directory\n";
next;
}
print "$_\n";
push @todo, $_;
}

recurse($_) for @todo;
}
 
G

gnari

Jay Sun Ex said:
I figured out the problem. Below is the working code. I also figured
out the answer to the module question. Thanks for all the help!

&recurse($ARGV[0]);

as other have mentioned, the '&' is not necessary.
recurse($ARGV[0]);
is considered better form (by many)

i assume your newsposter destroyed your indent.
sub recurse {
opendir DIR, $_[0];
foreach $file (readdir DIR) {
if (($file eq ".") or ($file eq "..")) {
#Do nothing
} elsif (-d ($_[0] . "\\" . $file)) {

alternatives:
} elsif (-d ($_[0] . '\' . $file)) {
or:
} elsif (-d "$_[0]\\$file") {
print "\n$file";

strange to see you print "\n" *before* each string
...
foreach $newpath (@directories) {
shift @directories;
&recurse($newpath);

really strange way of doing this. I suggest you declare
my @directories;
at the start of the sub, and change this loop to:
recurse($_) for @directories;


gnari
 
K

Kevin Collins

Jay Sun Ex said:
I figured out the problem. Below is the working code. I also figured
out the answer to the module question. Thanks for all the help!

&recurse($ARGV[0]);

as other have mentioned, the '&' is not necessary.
recurse($ARGV[0]);
is considered better form (by many)

i assume your newsposter destroyed your indent.
sub recurse {
opendir DIR, $_[0];
foreach $file (readdir DIR) {
if (($file eq ".") or ($file eq "..")) {
#Do nothing
} elsif (-d ($_[0] . "\\" . $file)) {

alternatives:
} elsif (-d ($_[0] . '\' . $file)) {
or:
} elsif (-d "$_[0]\\$file") {

It should also be mentioned that Perl does not care about '\' versus '/' in
file paths. So, unless you specifically need to use '\' (for example, a
system() call might need them), you can just use '/' and avoid the escaping:

} elsif (-d "$_[0]/$file") {
strange to see you print "\n" *before* each string


really strange way of doing this. I suggest you declare
my @directories;
at the start of the sub, and change this loop to:
recurse($_) for @directories;

Kevin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,159
Messages
2,570,879
Members
47,416
Latest member
LionelQ387

Latest Threads

Top