打印给定目录的文件和子目录

Print files and subdirectories of given directory

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

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);
}

使用此 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 =~ /.*\/(.*)/ &&  =~ /\./){
            #push @fileList, $File::Find::name."\n";
        }

readdir returns只有节点名,没有任何路径信息。如果没有指定路径,文件测试操作符将在当前工作目录中查找,并且因为当前目录不是 $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 $!;
}

已回答,但有时不关心实现细节很方便,您可以使用一些 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 文件,等等......这样的 "files" 通常可以作为普通文件打开和读取,因此 somethimes 而不是 -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 真的值得安装。