【问题标题】:Count number of files in a folder with Perl使用 Perl 计算文件夹中的文件数
【发布时间】:2016-10-15 18:00:20
【问题描述】:

我想用 Perl 计算一个文件夹中的文件数。使用下面的代码我可以列出它们,但是如何在 Perl 中计算它们呢?

$dir = "/home/Enric/gfs-0.5.2016061400";
opendir(DIR, "$dir");
@FILES = grep { /gfs./ } readdir(DIR);
foreach $file (@FILES) {
    print $file, "\n";
}
closedir(DIR);

【问题讨论】:

  • 与 perl 一样,将use strict; use warnings; 添加到顶部。 $#FILES 是一种计算文件数量的方法,但由于数组从 0 开始,您需要向其添加 1
  • 这段代码是你自己写的,还是从别处抄来的?我问的原因是因为它有很多问题,如果您从教程网站或其他地方复制它,我建议您以后避免使用该网站。
  • @FILES = grep { /gfs./ } readdir(DIR);打印连接“\n”,标量(@FILES);
  • @KeepCalmAndCarryOn:将$#array 用于获取@array 中的最后一个索引之外的任何内容都会使事情变得不必要地复杂化。为什么不在标量上下文中使用@array

标签: perl


【解决方案1】:

如果您只想对它们进行计数,一旦您打开了一个可供阅读的目录,您就可以操作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会返回该列表中的元素数。

【讨论】:

    【解决方案2】:

    您在@FILES 中有文件列表。所以你的问题变成了“我如何获得数组的长度?”这很简单,您只需在标量上下文中评估数组。

    my $number_of_files = @FILES;
    print $number_of_files;
    

    或者您可以使用scalar() 函数消除不必要的标量变量。

    print scalar @FILES;
    

    【讨论】:

    • 感谢 cmets @Dave Cross
    【解决方案3】:

    试试这个代码作为初学者(这是在 Windows 上,将包括 ... 和文件夹。如果你只想要文件,可以过滤掉这些):

    #!/usr/bin/perl -w
    
    my $dirname = "C:/Perl_Code";
    my $filecnt = 0;
    
    opendir (DIR, $dirname) || die "Error while opening dir $dirname: $!\n";
    while(my $filename = readdir(DIR)){
         print("$filename\n");
         $filecnt++;
    }
    closedir(DIR);
    print "Files in $dirname : $filecnt\n";
    exit;
    

    【讨论】:

      【解决方案4】:

      我知道这不在 Perl 中,但如果您需要快速的方法,只需在 bash 命令行中输入:

       ls -1 | wc -l 
      

      ls -1 为您提供目录中文件的列表,wc -l 为您提供行数。结合起来,它们将为您提供目录中的文件数量。

      或者,您可以使用

      从 Perl 调用 bash(尽管您可能不应该这样做)
      system("ls -1 | wc -l"); 
      

      【讨论】:

        猜你喜欢
        • 2016-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多