如何按照顺序读取目录中的文件?

9
当我使用Perl中的opendirreaddirclosedir函数读取目录时,readdir函数似乎没有按照任何特定顺序读取文件(据我所知)。
我正在读取一个以纪元时间戳命名的子目录的目录:
1224161460
1228324260
1229698140

我想按数字顺序读取这些目录,这将首先放置最旧的目录。
当我使用readdir时,它读取的第一个是1228324260,即中间那个。我知道我可以将目录内容放入数组并对数组进行排序,但是否有选项可以传递给readdir以按排序顺序读取?或者有没有更优雅的方法来完成此操作,而不是将所有内容推入数组并对数组进行排序?可能还有模块可以做到这一点,但在我们的环境中安装模块很困难,因此除非它是内置模块,否则我不想使用模块...
谢谢!
编辑 如要求,我发布了我正在使用的代码:
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);

我正在使用 Perl 5.8.2 在 AIX 系统上,如果这有所不同。 - BrianH
我知道这是一个简单的脚本,但你能否发布你的代码,这样我们就不必重新编写它了吗? - Bill the Lizard
1
在 process_dir 前面加上 & 符号并不是必要的,而且在某些情况下可能会有害。我建议你将其删除。 - Leon Timmermans
2
你不应该使用 "method() || die";当你将方法调用链接在一起时,应该使用 'or' 运算符。例如,"opendir() or die "Cannot open""。 - Robert P
谢谢Robert - 我会开始使用“或”而不是 - BrianH
3个回答

14

readdir可以在数组上下文中调用,因此只需这样做:

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 ) {
...

这里不需要 { $a <=> $b } 块; 默认即可。 - Svante
2
不,默認值是不夠的。默認排序是按字典順序排序,而不是按數字大小排序。在2001-09-09T01:46:40之前的時間戳將被排序為晚於之後大多數時間戳,因為它突破了10億秒的界限。 - Leon Timmermans
实际上,在这里使用 opendir 是过度的。你最好使用 glob() ... 不需要担心目录句柄。 - Robert P
@RobertP 使用 glob 你会得到完整的文件名(除非 $data_dir 恰好也是根目录)。这意味着需要额外调用 basenamechdir。后者可能会在时间上成为一个问题。 - Pavel
但是,readdir 也可能不好,例如对于文件测试 -X,因为你会失去路径。 - Pavel

5
你可以尝试使用一些Glob魔法,Glob似乎以排序方式运行,所以这样做:
#  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. 
}

或者

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

应该是有效的。只要小心使用glob,因为这个符号可能会造成问题:
 for(0..20){ 
   printf "%30s|%30s\n", glob($dir.'/*' ), glob($dir.'/*' );
 }

这段代码似乎有点神奇,它会将目录内容打印两次并显示在同一行。例如:

/foo/bar/a  |  /foo/bar/a
/foo/bar/b  |  /foo/bar/b
/foo/bar/c  |  /foo/bar/c
/foo/bar/d  |  /foo/bar/d

1
这可能是最好的方法。在这里使用 opendir 真的有点过头了。 - Robert P

5

只需要在您想要重新排序的任何列表运算符前面加上sort。 您不需要将结果存储在数组中。 您可以使用 foreach

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

foreach my $file ( sort { $a <=> $b } readdir $dh )
    {
    ...
    }

++ 为了简化,但我认为有人破坏了你的代码标记(排序块)。 - converter42
啊,是的,虽然在预览中显示得很好看,但是pre块不喜欢尖括号。 - brian d foy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接