如果您只想对它们进行计数,一旦您打开了一个可供阅读的目录,您就可以操作context,以便readdir 返回所有条目的列表,然后将其分配给一个标量。这给了你列表的长度,即。元素个数
opendir my $dh, $dir;
my $num_entries = () = readdir($dh);
构造= () = 将列表上下文强加于readdir 并将(该表达式†)分配给一个标量,从而获得该列表中的元素数。‡ § 见it in perlsecret。另见this page。
当然还有更清晰的方法,如下所示。
如果您想计算某些类型的文件,请先将文件列表传递给grep,就像您一样。由于grep 将列表上下文强加于其输入,readdir 返回所有文件的列表,并且在过滤后grep 本身返回一个列表。当您将其分配给标量时,您将获得该列表的长度(元素数),即。你的计数。例如,对于所有常规文件和/gfs./ 文件
use warnings;
use strict;
my $dir = '/home/Enric/gfs-0.5.2016061400';
opendir my $dh, $dir or die "Can't open $dir: $!";
my $num_files = grep { -f "$dir/$_" } readdir($dh);
rewinddir($dh); # so that it can read the dir again
my $num_gfs = grep { /gfs./ } readdir($dh);
(这只是一个示例,使用rewinddir 使其按原样工作。要真正从目录中获取两种文件,最好一次遍历条目并在此过程中对其进行排序,或者将所有文件读入一个数组,然后处理)
请注意,readdir 返回的是裸文件名,没有任何路径。 因此,对于通常对文件执行的大部分操作,我们需要在其前面加上路径(除非你先 chdir 到该目录)。这是在上面的 grep 块中完成的,因此 -f file test (-X) 具有正确的文件名。
如果您需要使用文件列表本身,请将其放入数组中,然后将其分配给标量
# Get the file list, then its length
my @files_gfs = map { "$dir/$_" } grep { /gfs./ } readdir($dh);
my $num_gfs = @files_gfs;
这里map 为每个文件构建完整路径。如果您不需要路径丢弃map { }。请注意,通常不需要在数组上正式使用scalar 来获取计数,例如
my $num_gfs = scalar @files_gfs; # no need for "scalar" here!
相反,只需将数组分配给一个标量,这是一个习惯用法(至少可以这么说)。
如果您在阅读过程中处理文件,请在执行过程中计数
my $cnt_gfs = 0;
while (my $filename = readdir($dh)) {
$cnt_gfs++ if $filename =~ /gfs./;
# Process $dir/$filename as needed
}
这里readdir 在标量上下文中(因为它的输出被分配给一个标量),它遍历目录条目,一次返回一个。
一些笔记
-
在上面的所有代码中,我都使用了问题中的示例,/gfs./——但如果这实际上意味着一个文字句点,那么它应该被替换为 /gfs\./
-
glob(或者更好的File::Glob)不需要所有关于readdir如何返回裸文件名(无路径)的讨论,它确实返回了完整路径
use File::Glob ':bsd_glob'; # (better with this)
my @files = glob "$dir/*";
这将返回路径为$dir/filename 的文件列表。
并不是说opendir+readdir 有任何错误。只是不要忘记路径。
另一种选择是使用库,例如 Path::Tiny 及其 children 方法。
† 赋值 () = readdir $dh 本身也返回一个值,在这种情况下,那个整个表达式(赋值)被放置在标量上下文中。
‡ 问题是 Perl 中的许多工具都依赖于它们的操作并返回上下文,因此人们不能总是仅仅将列表分配给标量并期望获得列表的长度. readdir 就是一个很好的例子,它返回列表上下文中所有条目的列表,但在标量上下文中返回单个条目。
§这是另一个技巧
my $num_entries = @{ [ readdir $dh ] };
这里是匿名数组(引用)[] 的构造函数,它将列表上下文强加于 readdir,而取消引用 @{ } 不关心上下文,只返回那个数组引用。所以我们可以将它分配给一个标量,这样scalar assignment会返回该列表中的元素数。