【问题标题】:Detect empty directory with Perl用 Perl 检测空目录
【发布时间】:2011-05-28 11:12:46
【问题描述】:

在 perl 中测试文件夹是否为空的简单方法是什么? -s 和 -z 不起作用。

例子:

#Ensure Apps directory exists on the test PC.
if ( ! -s $gAppsDir )
{ 
    die "\n$gAppsDir is not accessible or does not exist.\n"; 
}

#Ensure Apps directory exists on the test PC.
if ( ! -z $gAppsDir )
{ 
    die "\n$gAppsDir is not accessible or does not exist.\n"; 
}

上面这些,不能正常告诉我文件夹是空的。谢谢!


谢谢大家!我最终使用了:

sub is_folder_empty { my $dirname = shift; opendir(my $dh, $dirname) or die "Not a directory"; 
return scalar(grep { $_ ne "." && $_ ne ".." } readdir($dh)) == 0; }

【问题讨论】:

    标签: perl file directory


    【解决方案1】:

    为了清楚起见,有点冗长,但是:

    sub is_folder_empty {
        my $dirname = shift;
        opendir(my $dh, $dirname) or die "Not a directory";
        return scalar(grep { $_ ne "." && $_ ne ".." } readdir($dh)) == 0;
    }
    

    那么你可以这样做:

    if (is_folder_empty($your_dir)) {
        ....
    }
    

    【讨论】:

    • re "Not a directory": 或者不存在或者没有权限等等or die "$dirname: $!"如何@
    • @Zaid:您似乎混淆了“清晰”和“简洁”。此外,touch dir/.... 会欺骗您的正则表达式。
    • 清晰和简洁是好的。该解决方案确实读取了目录中的所有文件名,而最多需要三个条目('.' 和 '..' 可能首先出现,下一个名称表示目录不为空。修复它会破坏清晰度,但.
    • 不应该关闭目录句柄吗? closedir($dh)
    • @Andre perl 将在$dh 超出范围时关闭文件句柄,即当is_folder_empty 返回时。
    【解决方案2】:

    使用grep { ! /^[.][.]?\z/ } readdir $dir_h 可能会影响性能,以防检查执行多次并且某些目录可能有很多文件。

    最好在找到... 以外的目录条目时短路。

    在带有 ActiveState perl 5.10.1 的 Windows XP 上,以下 sub 似乎比我的 $HOME 上的 grep 方法快两倍:

    sub is_dir_empty {
        my ($dir) = @_;
    
        opendir my $h, $dir
            or die "Cannot open directory: '$dir': $!";
    
        while ( defined (my $entry = readdir $h) ) {
            return unless $entry =~ /^[.][.]?\z/;
        }
    
        return 1;
    }
    

    【讨论】:

    • @ysth return ! defined List::Util::first { $_ !~ /^[.][.]?\z/ } readdir $h; 该解决方案的缺点是,它将首先将整个目录条目读取到列表中。对于有许多成员的目录,这可能会很慢。
    【解决方案3】:

    或者没有任何 grepping 或正则表达式 - 这排除了任何奇怪的文件名意外获得的机会。另外,我的测试速度稍快。

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    sub is_dir_empty {
        return -1 if not -e $_[0];   # does not exist
        return -2 if not -d $_[0];   # in not a directory
        opendir my $dir, $_[0] or    # likely a permissions issue
            die "Can't opendir '".$_[0]."', because: $!\n";
        readdir $dir;
        readdir $dir;
        return 0 if( readdir $dir ); # 3rd times a charm
        return 1;
    }
    
    my @folders = qw( ./ ./empty ./hasonefile ./hastwofiles ./doesnotexist ./afile );
    for my $folder ( @folders ) {
        print "Folder '$folder' ";
        my $rc = is_dir_empty( $folder );
        if( $rc == -1 ) {
            print "does not exist\n";
        } elsif( $rc == -2 ) {
            print "is not a directory\n";
        } elsif( !$rc ) {
            print "is not empty\n";
        } else {
            print "is empty\n";
        }
    }
    

    很简单。如果您从对 readdir 的调用中获得三个有效响应,那么您就知道其中一定有一个文件。无论文件可能具有什么名称 - 或处理文件的顺序。会更喜欢名为“is_dir_used”的东西,因为我个人不喜欢双负函数名称和返回值。

    【讨论】:

    • 这应该是公认的解决方案。它是唯一不读取整个目录内容并且不应用不需要的正则表达式的解决方案(正如此解决方案所证明的那样)。我想人们可能会担心,因为这段代码 sn-p 比其他代码长,但这只是因为此外这个解决方案还显示了正确的错误处理,在我看来是另一个优势。
    • @josch 不,需要对要忽略的名称进行测试。例如,并非所有文件系统在每个目录中都有一个 ..。
    • @ysth .. 条目不是操作系统的属性而不是文件系统的属性吗?哪些没有?
    • @josch 一个符合 ISO 或兼容的操作系统,应该同时支持 '.'和 '..' 用于任何可挂载的文件系统,几乎涵盖了所有现代操作系统。我见过并使用过不支持这些的系统 - 可能是 25 多年前,而且这些系统都不支持 Perl - 所以这个解决方案今天应该没问题。
    • 检查第三个返回值是否已定义更安全——如果目录恰好包含一个名为 0 的文件,if readdir $dh 可能会失败。
    【解决方案4】:

    还有来自 cpan 的File::List。在这里有点过分了,但是对于稍微复杂的请求来说可以很方便,比如测试一个目录是否为空,这意味着它只包含空目录(即:不是文件)。

    【讨论】:

      【解决方案5】:
      opendir(DIR,"DIR PATH") or die "Unable to open directory \"DIR PATH\" \n";
      my @drList = readdir(DIR);
      close(DIR);
      if( grep(/\w/,@drList) ){ print "Not Empty\n" }
      else { print "Empty\n" }
      

      【讨论】:

      • 这将在非空但包含与/\w/ 不匹配的文件的目录上失败。
      【解决方案6】:
        sub is_folder_empty {
              my $dirname = shift;
              my @files = File::Find::Rule->file()->name('*')->maxdepth(1)->in("$dirname");
              return $#files < 0;
          }
      

      【讨论】:

      • 这对于包含与* 不匹配的文件的非空目录将失败。例如以点开头的文件。
      【解决方案7】:

      Credit to DevShed

      if (scalar <directory/*>) {print qq|File Exists\n|}
      

      编辑

      要包含隐藏文件:

      @arr = <directory/* directory/.*>;
      @arr = grep {!/^directory/[.]{1,2}$/} @arr;
      if (@arr) { print qq|File or Directory Exists\n| }
      

      请阅读 cmets,因为有很好的观点。尽管这个答案得到了否定的分数,但它仍然是正确的。

      【讨论】:

      • 坏坏坏。除非您真的知道自己在做什么,否则永远不要在标量上下文中使用 glob。该行,如果重复调用,将为目录中的每个事物返回一次 true,然后将返回一次 false,然后重新开始。
      • 我认为这不是真的。我认为它会在找到的第一个文件处停止,如论坛中所述,它类似于-e 并且相当于调用 w/o scalar,两者都将停止在找到的第一个文件处。 never use glob in scalar context unless you really know what you are doing 在这方面是绝对安全的。为了不做而不做是不好的建议。
      • 试试吧。以下将打印 File Exists 两次,而不是三次:mkdir directory; touch directory/a; touch directory/b; perl -we'for(1..3){if (scalar &lt;directory/*&gt;) {print qq|File Exists\n|}}'
      • 正确的做法是if (()=&lt;directory/*&gt;),但仍然存在无法注意到目录/.foo 等文件的缺陷
      • ysth:这是真的,我从不否认。我也同意,您应该知道自己在做什么,但这是测试目录是否为空的完美有效方法(它不会检查您提到的隐藏文件)。 -- 你的例子是一个循环,但是如果你做了$x=&lt;directory/*&gt;;$y=&lt;directory/*&gt;; 那么$x$y 将是同一件事,句柄被调用,否则(在循环中)句柄保持打开并检索接下来的事情,所以而不是for 最好使用while - 将到达文件末尾。
      猜你喜欢
      • 2012-01-20
      • 2013-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-14
      相关资源
      最近更新 更多