【问题标题】:Print files and subdirectories of given directory打印给定目录的文件和子目录
【发布时间】:2016-08-17 19:20:36
【问题描述】:

我正在尝试从给定目录中获取所有文件和目录,但我无法指定类型(文件/目录)。没有打印任何内容。我做错了什么以及如何解决它。代码如下:

sub DoSearch {
    my $currNode = shift;
    my $currentDir = opendir (my $dirHandler, $currNode->rootDirectory) or die $!;

    while (my $node = readdir($dirHandler)) {
        if ($node eq '.' or $node eq '..') {
            next;
        }

        print "File: " . $node . "\n" if -f $node;
        print "Directory " . $node . "\n" if -d $node;
   }

   closedir($dirHandler);
}

【问题讨论】:

    标签: perl file subdirectory readdir


    【解决方案1】:

    使用此 CPAN 模块以递归方式获取所有文件和子目录。

        use File::Find;        
    
        find(\&getFile, $dir);
        my @fileList;
    
        sub getFile{
            print $File::Find::name."\n";
            # Below lines will print only file name. 
            #if ($File::Find::name =~ /.*\/(.*)/ && $1 =~ /\./){
                #push @fileList, $File::Find::name."\n";
            }
    

    【讨论】:

    • 我认为我们应该先使用 CPAN 模块而不是编写我们自己的代码,因为它已经过优化并且如果确实解决了问题。
    • 这几乎无关紧要。您可能认为 OP 应该改用 Python,但这并不能使 Python 解决方案变得有用。您没有帮助 OP 了解他自己的代码有什么问题,并且当需要非递归解决方案时,您的解决方案会递归.您的输出甚至与原始代码生成的完全不同,并且您的答案中有一个未使用的数组和一些杂乱的注释部分。这是一个糟糕的解决方案
    • 是的,这是真的,您给出的答案解决了他的问题,但您还使用了另一个核心 CPAN 模块。我刚刚给出了另一种方式。
    【解决方案2】:

    readdir 只返回节点名,没有任何路径信息。如果没有指定路径,文件测试操作符将在当前工作目录中查找,并且因为当前目录不是$currNode->rootDirectory,所以它们不会被找到

    我建议您使用File::Spec::Functions 核心模块中的rel2abs 将节点名称与路径结合起来。您可以使用字符串连接,但库函数会处理一些极端情况,例如目录是否以斜杠结尾

    还值得指出的是,Perl 标识符最常使用snake_case,熟悉该语言的人会感谢您不使用大写字母。对于标识符的第一个字符,尤其应避免使用它们,因为这样的名称是为包名称等全局变量保留的

    我认为你的子程序应该是这样的

    use File::Spec::Functions 'rel2abs';
    
    sub do_search {
        my ($curr_node) = @_;
        my $dir         = $curr_node->rootDirectory;
    
        opendir my $dh, $dir or die qq{Unable to open directory "$dir": $!};
    
        while ( my $node = readdir $dh ) {
            next if $node eq '.' or $node eq '..';
    
            my $fullname = rel2abs($node, $dir);
    
            print "File:     $node\n" if -f $fullname;
            print "Directory $node\n" if -d $fullname;
       }
    }
    

    另一种方法是将当前工作目录设置为正在读取的目录。这样就无需操作文件路径,但您需要在更改之前和之后保存和恢复原始工作目录

    Cwd 核心模块提供getcwd,您的代码将如下所示

    use Cwd 'getcwd';
    
    sub do_search {
        my ($curr_node) = @_;
    
        my $cwd = getcwd;
        chdir $curr_node->rootDirectory or die $!;
    
        opendir my $dh, '.' or die $!;
    
        while ( my $node = readdir $dh ) {
            next if $node eq '.' or $node eq '..';
    
            print "File: \n" if -f $node;
            print "Directory $node\n" if -d $node;
       }
    
       chdir $cwd or die $!;
    }
    

    【讨论】:

    • 感谢您提供的解决方案。第一个解决了我的问题:)
    【解决方案3】:

    已经回答,但有时不用关心实现细节也很方便,您可以使用一些 CPAN 模块来隐藏这些细节。

    其中一个是美妙的Path::Tiny 模块。

    您的代码可能是:

    use 5.014;         #strict + feature 'say' + ...
    use warnings;
    use Path::Tiny;
    
    do_search($_) for @ARGV;
    
    sub do_search {
            my $curr_node = path(shift);
            for my $node ($curr_node->children) {
                    say "Directory  : $node" if -d $node;
                    say "Plain File : $node" if -f $node;
            }
    }
    

    children 方法自动排除了...

    您还需要了解-f 测试仅适用于真正的files。因此,上面的代码排除了例如symlinks(其指向真实文件)或FIFO 文件,等等......这样的“文件”通常可以作为普通文件打开和读取,因此有些时候而不是-f 可以方便地使用 -e && ! -d 测试(例如存在,但不是目录)。

    Path::Tiny 对此有一些方法,例如你可以写

            for my $node ($curr_node->children) {
                    print "Directory  : $node\n" if $node->is_dir;
                    print "File       : $node\n" if $node->is_file;
            }
    

    is_file 方法通常是 DWIM - 例如是否:-e && ! -d

    使用Path::Tiny,您还可以使用iterator 方法轻松扩展您的函数以遍历整个树:

    use 5.014;
    use warnings;
    use Path::Tiny;
    
    do_search($_) for @ARGV;
    
    sub do_search {
    
        #maybe you need some error-checking here for the existence of the argument or like...
    
        my $iterator = path(shift)->iterator({recurse => 1});
        while( my $node = $iterator->() ) {
            say "Directory  : ", $node->absolute if $node->is_dir;
            say "File       : ", $node->absolute if $node->is_file;
        }
    }
    

    上面打印了所有文件和目录的类型,从给定的参数向下递归......

    等等...Path::Tiny 真的值得安装。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 2012-04-02
      • 2017-03-15
      • 1970-01-01
      • 2016-05-16
      • 2021-03-26
      • 1970-01-01
      相关资源
      最近更新 更多