How can I read the files in a directory in sorted order?
Asked Answered
P

3

9

When I read a directory in Perl with opendir, readdir, and closedir, the readdir function doesn't seem to read the files in any specific order (that I can tell).

I am reading a directory that has subdirectories named by epoch timestamp:

1224161460
1228324260
1229698140

I want to read in these directories in numerical order, which would put the oldest directories first.

When I use readdir, the first one it reads is 1228324260, which is the middle one. I know I could put the directory contents in an array and sort the array, but is there an option I can pass to readdir to read in sorted order? Or maybe a more elegant way of accomplishing this than pushing everything into array and sorting the array? There are probably modules out there to do this too, but it is difficult to get modules installed in our environment, so unless it is a built-in module I'd prefer to not use modules...

Thanks!

EDIT As requested, I am posting the code that I am using:

opendir( my $data_dh, $data_dir ) or die "Cannot open $data_dir\n";
while ( my $name = readdir($data_dh) ) {
    next if ( $name eq '.' or $name eq '..' );
    my $full_path = "${data_dir}/${name}";
    next unless ( -d $full_path );
    process_dir($full_path);
}
closedir($data_dh);
Pricking answered 12/1, 2009 at 17:22 Comment(5)
I am using perl 5.8.2 on an AIX box, if it makes a differencePricking
I know it's a simple script, but can you post your code so we don't have to reproduce it?Befit
The & sign before process_dir is not necessary, and can in some circumstances be harmful. I would advice you to remove it.Senhor
You should not do "method() || die"; the 'or' operator is what you want when you chain method calls together. Eg, "opendir() or die "Cannot open"Pickax
Thanks Robert - I will start using "or" insteadPricking
I
15

readdir can be called in array context, so just do this:

opendir( my $data_dh, $data_dir) or die "Cannot open $data_dir\n";
my @files = sort { $a <=> $b } readdir($data_dh);
while ( my $name = shift @files ) {
...
Inheritance answered 12/1, 2009 at 17:42 Comment(5)
You don't need the { $a <=> $b } block; the default suffices here.Clown
NO, the default does NOT suffice. The default sorts lexically, not numerically. Timestamps preceding 2001-09-09T01:46:40 would sort as later than most timestamps after it, because it broke the 1,000,000,000 seconds barrier.Senhor
Really though, opendir is overkill here. Your best bet would be to use glob() ... no directory handles to worry about.Pickax
@RobertP With glob you're gonna get full names (unless $data_dir happens to be also a root one). That means one additional call to basename or chdir. Which the latter might be a problem in time.Separation
But then readdir might be bad as well, i.e. for file tests -X as you loose paths though.Separation
U
5

You can try it with a bit of Glob magic, glob appears to function in a sorted manner, so this:

#  Glob in scalar context iterates the result set internally
while( defined( my $dir = glob($dir . '/*' ) ) ){ 
     print $dir, "\n";
    # $dir is fed ordered and with full names. 
}

or

# Glob in list context returns all results. 
for( glob($dir.'/*' ) ){ 
  print $dir , "\n";
  # also ordered. 
}

should work. Just be careful with glob, because this:

 for(0..20){ 
   printf "%30s|%30s\n", glob($dir.'/*' ), glob($dir.'/*' );
 }

does something semi-magical, and prints the dir contents twice on each line. ie:

/foo/bar/a  |  /foo/bar/a
/foo/bar/b  |  /foo/bar/b
/foo/bar/c  |  /foo/bar/c
/foo/bar/d  |  /foo/bar/d
Unalloyed answered 12/1, 2009 at 18:0 Comment(1)
This is probably the best way to do it. opendir is really overkill here.Pickax
J
5

Just throw a sort in front of any list operator that you want to re-order. You don't need to store the results in an array either. You can use a foreach:

opendir my($dh), $dirname or die "Could not open directory [$dirname]: $!";

foreach my $file ( sort { $a <=> $b } readdir $dh )
    {
    ...
    }
Justitia answered 12/1, 2009 at 19:10 Comment(2)
++ for simplification, but I think someone broke your code markup (the sort block).Gathard
Ah, yeah, the pre blocks don't like the angle brackets, even though it comes out nicely in the preview.Justitia

© 2022 - 2024 — McMap. All rights reserved.