Perl readdir作为一行代码实现?

3

目前我知道在 Perl 中有两种方法可以打开和读取一个目录。你可以使用 opendirreaddirclosedir,或者简单地使用 glob 来获取目录的内容。


示例:

使用 opendirreaddirclosedir

opendir my $dh, $directory;
my @contents = readdir $dh;
closedir $dh;

使用 glob

my @contents = <$directory/*>;

我得知,在某些情况下,glob 方法可能会产生不可预测的结果(例如,在遇到目录名称中的特殊字符时,它在不同的操作系统上可能会有所不同)。

我喜欢 glob 方法的原因是它非常 "快速和简单"。只需一行代码就可以完成工作,但如果它不能在所有情况下工作,那么就会引入意外和难以发现的错误。

我想知道是否有一种类似于使用 opendirreaddirclosedir 方法的 "速记" 方式?

也许像我最近发现的一行代码读取文件的高级方法这样的东西。


这是一个关于glob和readdir的绝佳文章:https://dev59.com/vnI_5IYBdhLWcg3wJfgM - Andy Lester
@AndyLester,基本上glob无法找到名称中带有特殊字符的文件/目录,除非这些特殊字符被正确转义。如何转义特殊字符可能取决于操作系统。 - tjwrona1992
但是现在我想起来了,那些特殊字符不也有可能会使opendir出现问题吗? - tjwrona1992
子目录内容 { my ($path) = @_; opendir my $dh, $path or die $!; return readdir $dh }。;) 我知道这很诱人,但请不要陷入总是试图在一行中完成所有事情的陷阱。这往往会使您的代码难以阅读、调试和维护。 - ThisSuitIsBlackNot
1
@Andy Lester,这没有意义;除了当 <...> 表示 readline(...) 时,<...>glob(qq<...>) 没有任何区别。 - ikegami
显示剩余7条评论
2个回答

2
下面这个怎么样?
my @contents = get_dir_contents($dir);

你甚至可以决定如何处理错误,是否返回.,是否返回..,是否返回“隐藏”文件,以及是否在文件名前面添加路径,因为你自己编写了get_dir_contents


替代方案:

  • use File::Find::Rule qw( );
    my @contents = File::Find::Rule->maxdepth(1)->in($dir);
    
  • use File::Slurp qw( read_dir );
    my @contents = read_dir($dir);
    
  • # Unix only, but that includes cygwin and OS/X.
    my @contents = <\Q$dir\E/.* \Q$dir\E/*>;
    

我喜欢File::Slurp::read_dir,尽管我知道File::Slurp在某些圈子中已经不再受欢迎。 - Jim Davis
1
@Jim Davis,File :: Slurp曾经非常有问题,所以我一直避免使用它。我甚至不知道它有这样的子程序。但是据我了解,这些问题现在早已得到解决。我已将其添加到我的答案中。 - ikegami
@ikegami,子程序是显而易见的解决方案,这更多是一个理论问题。我想要最简单、最基本的读取目录的方法。基本上它只会给你readdir的默认输出,而不会有所有笨重的操作,比如先用opendir获取句柄,然后用readdir读取句柄,最后用closedir关闭句柄。 - tjwrona1992
1
我给了你四个这样的解决方案。 - ikegami
get_dir_contents函数在哪个包中? - felwithe
显示剩余2条评论

0

我相信我已经想出了一个有效的一行代码,其中涉及到opendir/readdir

my @contents = do { opendir my $dh, $dir or die $!; readdir $dh };

这应该打开并读取目录,返回其所有内容,并在do块结束时,$dh应由Perl "automagically"关闭。


这与 opendir my $dh, $dir; my @contents = readdir $dh; 在实质上有何不同?你无需使用 do将多个语句放在一行上。 - ThisSuitIsBlackNot
1
do {} 使 my ($dh) 局部化,因此文件句柄将在超出范围时关闭。它还允许声明 my @contents 在该范围之外,因此可以在同一行上声明并仍然在非局部中使用。如果您尝试在没有 do {} 的情况下进行本地范围限制,则会得到 { opendir my $dh, $dir; my @contents = readdir $dh; },但是接下来的一行无法使用 @contents,因为它现在已经超出了范围!...实际上非常美丽和优雅。Perl 真是太棒了。:) - tjwrona1992
不立即关闭词法目录句柄通常是不重要的,所以我认为你发布的内容与将多个语句放在一行上没有实质性的区别。但既然你已经知道这是不良习惯,我就不再唠叨了 :) - ThisSuitIsBlackNot
OP特别要求比opendir+readdir+closedir更短的东西。从技术上讲,去掉closedir确实更短,但你不认为OP知道这一点吗?此外,已经提供了其他四个更短的解决方案,所以我不确定你如何认为这是一种速记方式。 - ikegami
创建自己的子程序几乎不是速记方式,使用另一个模块几乎就像编写自己的子程序一样(除了它已经过测试,你知道它会工作)。在你的答案中,使用File::Slurp可能是最短和最简单的实现,但这个问题的目的是找到我自己的方法来解决它。这更多地是关于学习而不是实际可用性。基本上,我知道我可以使用glob在一行中得到它,但我想知道是否有一个使用opendir/readdir/closedir的简短替代方法。 - tjwrona1992
另外,我在原始答案中省略了 or die 的原因是为了强调代码的简洁和美观 :) 显然添加 or die 是更好的实践。 - tjwrona1992

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