Perl readdir in order
Asked Answered
A

4

9

Is there any way to guarantee an order from the list returned by readdir?

I have the code:

opendir(my $DIR, $src) or die "Error opening $src";

# Loop for each file in the directory
while (my $file = readdir($DIR))
{
        print "$file\n";
    }

But it returns in random order. Now I know there are plenty of solutions via a quick Google search, but I can't find the exact order I need. Basically I want the folders to appear FIRST or LAST, and not in between the files.

For example, right now if I have the folder structure:

folder
folder
file1
file2
file3

I get the result:

file2
folder
folder
file1
file3

When really I want:

folder
folder
file1
file2
file3

Or:

file1
file2
file3
folder
folder

Any way to achieve this?

Astronomical answered 28/5, 2013 at 13:13 Comment(1)
some kind of sort should be used.Pless
P
6

You can sort by putting folders first and then sorting by file/dir name,

# $src pointing to folder open with opendir
my @sorted_dir = 
  map $_->[0],
  sort {
    $a->[1] <=> $b->[1]
      ||
    $a->[0] cmp $b->[0]
  }
  map [ $_, -f "$src/$_" ],
  readdir($DIR);

While similar effect can be achieved with,

for my $file (sort { -f "$src/$a" <=> -f "$src/$b" } readdir($DIR)) {
  print "$file\n";
}

it's slower and inefficient as it more often goes to file system checking if directory entry is a plain file.

Pless answered 28/5, 2013 at 13:30 Comment(4)
Thanks, I was hoping for a more elegant solution though. I will fall back to this if all else fails :)Astronomical
Updated with simpler but slower alternative.Pless
Thanks, that's a nicer looking one. That was also the issue with the other solution provided by simbabque, it was missing $src/ and that's why it wasn't working!Astronomical
Also note that second one doesn't sort by entry names.Pless
C
5

You could use a sort to do it, by looking at each entry of the list returned by readdir.

opendir(my $DIR, '.') or die "Error opening ";

foreach my $file (sort { -d $a <=> -d $b } readdir($DIR)) {
  print "$file\n";
}

This will give folders last.

Complacence answered 28/5, 2013 at 13:26 Comment(4)
Hi, when I do this I get Use of uninitialized value in numeric comparison (<=>).Astronomical
Try cmp instead of <=>.Adelaideadelaja
Now I get Use of uninitialized value in string comparison (cmp). Removing the -d from both yields no errors (but obviously wrong output)..Astronomical
Looks like there is empty stuff in the return values of readdir. <=> is correct, btw. cmp is alphanumeric, and -d only returns 0 and 1, so no cmp needed.Complacence
I
5

foreach (sort readdir $dh) {} works fine for me.

For example:

opendir (my $DIR, "$dir") || die "Error while opening $dir: $!\n";

foreach my $dirFileName(sort readdir $DIR)
{
      next if $dirFileName eq '.' or $dirFileName eq '..';
      print("fileName: $dirFileName ... \n");
}
Imperceptible answered 19/3, 2015 at 13:18 Comment(0)
T
2

You can use part from List::MoreUtils

#!/usr/bin/env perl

use strict;
use warnings;

use List::MoreUtils 'part';

my $dir = shift || '.';

opendir my $dh, $dir or die "Cannot open $dir";

my ($files, $dirs) = part { -d } sort readdir $dh;

print "$_\n" for @$files, @$dirs;

For another idea, you might look at File::Next.

Teage answered 28/5, 2013 at 13:29 Comment(1)
It works, its easy to read, and you almost certainly have it already. But ... your choice.Teage

© 2022 - 2024 — McMap. All rights reserved.