readdir: is there a way to reset cursor to beginning?

P

Patrick H.

Hi, I am toying around with the directory operations chapter of
"Learning Perl" and ran into a bit of a snag.

I am trying to open a directory, output all the -f files to a @files
array and all the -d files to a @folders array. The problem is it
seems to only work for the first instance of readdir I use; they both
work individually when I comment the other out, but when I have them
both only the first array is populated. Do I need to closedir and then
opendir again, or is there a way for me to reset the cursor (if that
is even the right terminology)?

Below is my code, and advice would be appreciated.

Patrick H.


#!/usr/bin/perl -w
use strict;

# folder to scan
my $folder = 'c:/';

# open folder
opendir C_DRIVE, $folder or die "problem reading $folder: $!";

my @files = grep { -f "$folder/$_" } readdir(C_DRIVE); # all
files
my @folders = grep { -d "$folder/$_" } readdir(C_DRIVE); # all
folders

# close folder
closedir C_DRIVE;

# test results
print "your files: @files\n";
print "your folders: @folders\n";
 
J

Jürgen Exner

Patrick H. said:
I am trying to open a directory, output all the -f files to a @files
array and all the -d files to a @folders array. The problem is it
seems to only work for the first instance of readdir I use; they both
work individually when I comment the other out, but when I have them
both only the first array is populated. Do I need to closedir and then
opendir again, or is there a way for me to reset the cursor (if that
is even the right terminology)?

You could try seek(). Although it is documented only for filehandles,
not directory handles. it just might work on directory handles, too.
Below is my code, and advice would be appreciated.
[...]
# open folder
opendir C_DRIVE, $folder or die "problem reading $folder: $!";

my @files = grep { -f "$folder/$_" } readdir(C_DRIVE); # all
files
my @folders = grep { -d "$folder/$_" } readdir(C_DRIVE); # all
folders

However a better approach would be to loop through all the files in a
single pass and assign them to @files or @folders as you read each
directory entry.

<sketch only, not actual code>

while (my $direntry = readdir (C_DRIVE))
if (-f $direntry) {push @files, $direntry};
if (-d $direntry) {push @folders, $direntry}
}


Or read then all into a one array, duplicate that array, and then filter

@folders = @files = readdir(...);
@folders = grep (-d ...., @folders);
@files = grep (-f ...., @files);

jue
 
P

Patrick H.

Hi, I am toying around with the directory operations chapter of
"Learning Perl" and ran into a bit of a snag.

I am trying to open a directory, output all the -f files to a @files
array and all the -d files to a @folders array. The problem is it
seems to only work for the first instance of readdir I use; they both
work individually when I comment the other out, but when I have them
both only the first array is populated. Do I need to closedir and then
opendir again, or is there a way for me to reset the cursor (if that
is even the right terminology)?

Below is my code, and advice would be appreciated.

Patrick H.

#!/usr/bin/perl -w
use strict;

# folder to scan
my $folder = 'c:/';

# open folder
opendir C_DRIVE, $folder or die "problem reading $folder: $!";

my @files = grep { -f "$folder/$_" } readdir(C_DRIVE);      # all
files
my @folders = grep { -d "$folder/$_" } readdir(C_DRIVE);    # all
folders

# close folder
closedir C_DRIVE;

# test results
print "your files: @files\n";
print "your folders: @folders\n";

With the risk of looking like I'm talking to myself, I came up with a
different way of doing it through just one readdir - figured I would
post what my solution ended up being.

-Patrick

# scope variables
my (@files,@folders);

# read directory
while (my $name = readdir C_DRIVE) {
push @files, $name if -f "$folder/$name";
push @folders, $name if -d "$folder/$name";
}
 
P

Patrick H.

However a better approach would be to loop through all the files in a
single pass and assign them to @files or @folders as you read each
directory entry.

<sketch only, not actual code>

        while (my $direntry = readdir (C_DRIVE))
                if (-f $direntry) {push @files, $direntry};
                if (-d $direntry) {push @folders, $direntry}
        }


Jue, thank you very much for the input. I came up with a similar
approach before I read your post. Always glad to have someone else's
opinion confirm what I was thinking :) .

Thanks,
Patrick
 
J

John W. Krahn

Patrick said:
Hi, I am toying around with the directory operations chapter of
"Learning Perl" and ran into a bit of a snag.

I am trying to open a directory, output all the -f files to a @files
array and all the -d files to a @folders array. The problem is it
seems to only work for the first instance of readdir I use; they both
work individually when I comment the other out, but when I have them
both only the first array is populated. Do I need to closedir and then
opendir again, or is there a way for me to reset the cursor (if that
is even the right terminology)?

perldoc -f rewinddir

Below is my code, and advice would be appreciated.

Patrick H.


#!/usr/bin/perl -w
use strict;

# folder to scan
my $folder = 'c:/';

# open folder
opendir C_DRIVE, $folder or die "problem reading $folder: $!";

my @files = grep { -f "$folder/$_" } readdir(C_DRIVE); # all
files
my @folders = grep { -d "$folder/$_" } readdir(C_DRIVE); # all
folders

my ( @files, @folders );
while ( my $file = readdir C_DRIVE ) {
push @files if -f "$folder/$file";
push @folders if -d "$folder/$file";
}

# close folder
closedir C_DRIVE;

# test results
print "your files: @files\n";
print "your folders: @folders\n";



John
 
U

Uri Guttman

JWK> perldoc -f rewinddir

i recalled that was in there. but it isn't needed as you note below

JWK> my ( @files, @folders );
JWK> while ( my $file = readdir C_DRIVE ) {
JWK> push @files if -f "$folder/$file";
JWK> push @folders if -d "$folder/$file";
JWK> }

i hate all the duplicate pushes. try this on for size (untested :):

use File::Slurp ;
push( @{ -d "$folder/$_" ? \@folders : \@files }, $_ ) for read_dir( 'C:' );

that assumes if it ain't a dir, it is a file.

uri
 
P

Peter Scott

i hate all the duplicate pushes. try this on for size (untested :):

use File::Slurp ;
push( @{ -d "$folder/$_" ? \@folders : \@files }, $_ ) for read_dir(
'C:' );

that assumes if it ain't a dir, it is a file.

As long as we're trying to reduce duplication and maintain clarity and
semantics, how about:

use File::Slurp;
my (@files, @folders);
my $folder = 'C:';
for ( map { "$folder/$_" } read_dir $folder )
{
my $dest = -d ? \@folders : -f ? \@files : '';
push @$dest, $_ if $dest;
}

or (equally untested, don't know if the glob pattern is right for a
Windows box, am not near one):

my (@files, @folders);
for ( glob 'C:/*.*' )
{
my $dest = -d ? \@folders : -f ? \@files : '';
push @$dest, $_ if $dest;
}
 
R

Randal L. Schwartz

Uri> i hate all the duplicate pushes. try this on for size (untested :):

Uri> use File::Slurp ;
Uri> push( @{ -d "$folder/$_" ? \@folders : \@files }, $_ ) for read_dir( 'C:' );

Uri> that assumes if it ain't a dir, it is a file.

OP said he was working through "Learning Perl". Probably hasn't
gotten to references yet, so you just blew his test score. :)

print "Just another Perl hacker,"; # the original
 
U

Uri Guttman

Uri> i hate all the duplicate pushes. try this on for size (untested :):

Uri> use File::Slurp ;
Uri> push( @{ -d "$folder/$_" ? \@folders : \@files }, $_ ) for read_dir( 'C:' );

Uri> that assumes if it ain't a dir, it is a file.

RLS> OP said he was working through "Learning Perl". Probably hasn't
RLS> gotten to references yet, so you just blew his test score. :)

well, you may have missed my smiley before the code. that wasn't
intended as a real useful answer. but the technique of selecting what
array to push onto is useful occaisionally.

uri
 

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
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top