【问题标题】:Getting names of directories under given path获取给定路径下的目录名称
【发布时间】:2010-12-14 03:08:37
【问题描述】:

我正在尝试获取给定路径下所有一级目录的名称。

我尝试使用File::Find,但遇到了问题。

有人可以帮我吗?

【问题讨论】:

    标签: perl


    【解决方案1】:

    使用-d文件检查运算符:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use autodie;
    
    my $path = $ARGV[0];
    die "Please specify which directory to search" 
        unless -d $path;
    
    opendir( my $DIR, $path );
    while ( my $entry = readdir $DIR ) {
        next unless -d $path . '/' . $entry;
        next if $entry eq '.' or $entry eq '..';
        print "Found directory $entry\n";
    }
    closedir $DIR;
    

    【讨论】:

    • 啊!他是在标点符号前使用空格的人之一。我真的以为他的意思是“给路径'。'”。我会相应地改变我的答案
    • 应该检查opendirclosedir的返回值。
    • 这就是你投反对票的原因?今天到底怎么了?
    • 好的。所以我编辑了这个来检查opendir的返回值。我拒绝检查 closedir 的返回值,除非有人告诉我脚本在 closeir 失败时应该做什么。
    • 是的,它可能会错过名为“0”的目录。由于我没有发布单元测试,我现在可以进行下一次投票吗?如果有人在脚本运行时拔掉硬盘的插头怎么办?
    【解决方案2】:

    如果不需要遍历整个目录层次结构,File::SlurpFile::Find 好用得多。

    use strict;
    use warnings;
    use File::Slurp qw( read_dir );
    use File::Spec::Functions qw( catfile );
    
    my $path = shift @ARGV;
    my @sub_dirs = grep { -d } map { catfile $path, $_ } read_dir $path;
    print $_, "\n" for @sub_dirs;
    

    如果您确实需要遍历层次结构,请检查 CPAN 以获得更友好的 File::Find 替代方案。

    最后,本着 TIMTOWTDI 的精神,这里有一些快速而低俗的东西:

    my @sub_dirs = grep {-d} glob("$ARGV[0]/*");
    

    【讨论】:

      【解决方案3】:
      use File::Spec::Functions qw( catfile );
      
      my ($path) = @ARGV;
      
      opendir my $DIR, $path 
          or die "Cannot open directory: '$path': $!";
      
      while ( my $entry = readdir $DIR ) {
          next if $entry =~ /\A\.\.?\z/;
          next unless -d catfile $path, $entry;
          print $entry, "\n";
      }
      
      closedir $DIR;
      

      这对我有用。

      【讨论】:

      • 你问的是'.'下的目录吗?就像您的问题暗示或者您是否在询问作为命令行参数给出的目录的子目录?
      • 作为命令行参数给出的目录的子目录
      • 我相应地改变了我的答案。
      • 你能解释一下这个正则表达式吗 plz /\A\.\.?\z/
      • 你需要检查readdir()的返回值,否则如果有一个名为0的文件就会中断。 (虽然这应该为 Perl 5.11.2 修复)
      【解决方案4】:

      我在 Windows XP 下运行 ActivePerl 5.10.1。如果我想获取根驱动器 F 下的所有目录名称。我将使用以下代码:

      #!perl
      opendir (DIR,'F:/');
      my @folder = readdir(DIR);
      foreach my $f (@folder)
      {
         next if ($f =~ /\./);
         print "$f\n";
       }
      

      嗯,这通常有效,因为我的文件夹名称不包含点。否则失败。

      好吧,似乎即使我的方法适用于我的情况,人们仍然会因为它有问题而投反对票。所以我不得不使用官方方法, -d 标志来检查文件是否是目录:

      升级后的代码:

      #!perl
      use strict;
      use warnings;
      
      opendir (DIR, "F:/");
      my @files = readdir(DIR);
      my @dirs = grep { -d } @files;
      print @dirs;
      

      【讨论】:

      • 我有一个名为“makefile”的文件,没有 .扩展名,它已包含在您的支票中。
      • 我的方法有问题。仅当文件名包含点扩展名且文件夹名称不包含点时才有效。看起来 Manni 的解决方案更适合您的情况。
      • 这不起作用。 @files 仅包含目录中每个条目的名称(不是完整路径!)。所以在它们上使用 -d 只会给你 '.'和 Windows 上的“..”。
      【解决方案5】:

      使用opendir-d

      【讨论】:

      • 我应该在按“添加评论”之前阅读我的帖子。我的意思是写-d
      【解决方案6】:

      您可以使用 find2perl 将您的 find 命令转换为 perl。有关详细信息,请参阅 perldoc find2perl。

      maxdepth 的解决方法:(来自 Randall 的参考)

      $  find2perl /home/path -type d -eval 'my $slashes = $File::Find::name =~ tr#/##;return $File::Find::prune = 1 if $slashes > 2;return if $slashes ==2'
      

      代码:

         use strict;
          use File::Find ();
          use vars qw/*name *dir *prune/;
          *name   = *File::Find::name;
          *dir    = *File::Find::dir;
          *prune  = *File::Find::prune;
          sub wanted;
      
          File::Find::find({wanted => \&wanted}, '/');
          exit;
          sub wanted {
              eval { my $slashes = $File::Find::name =~ tr#/##;return $File::Find::prune = 1 if $slashes > 1;return if $slashes ==1 };
              if ( $? == "0" && -d _  ){
                  print "$name --- \n";   
              }
          }
      

      输出

      $ pwd
      /temp
      
      $ tree
      .
      |-- dir1
      |   |-- subdir1
      |   |   |-- subsubdir1
      |   |   `-- testsubdir1.txt
      |   `-- testdir1.txt
      |-- dir2
      |   |-- subdir2
      |   |   |-- subsubdir2
      |   |   `-- testsubdir2.txt
      |   `-- testdir2.txt
      |-- dir3
      |   `-- subdir3
      |       `-- subsubdir3
      `-- test
      
      9 directories, 5 files
      
      $ perl perl.pl
      /temp ---
      /temp/dir3 ---
      /temp/dir1 ---
      /temp/dir2 ---
      

      【讨论】:

      • 我没有否决你的答案,但是:find2perl C:/ -maxdepth 1 给了Unrecognized switch: -maxdepth
      • “如果你敢”是什么意思?你会因为解释他的理由而惩罚他吗?
      • @manni,没有。只是一个心理挑战。因为这个网站不需要投票者强制评论。
      • @ghostdog74 说真的...您是否真的查看了生成的代码或尝试在其下有一棵大树的目录下运行它。它已经通过我的C:\Windows 大约一分钟了。我想说readdir 和/或File::Slurp::read_dir 非常好,这里真的不需要File::Find
      • 是的,有不止一种方法可以做到这一点。但并不是每一种可能的方式都是最好的(甚至是好的)。
      【解决方案7】:

      使用文件::查找::规则

      #!/usr/bin/perl --
      use strict;
      use warnings;
      
      use Shell::Command qw( rm_rf touch mkpath );
      use autodie;
      use File::Find::Rule;
      
      Main(@ARGV);
      exit(0);
      
      sub Main{
        use autodie;
        my $dir = "tmp";
        mkdir $dir;
      #~   chdir $dir;
        mkpath "$dir/a/b/c/d";
        mkpath "$dir/as/b/c/d";
        mkpath "$dir/ar/b/c/d";
      
        print `tree`;
        print "---\n";
        print "$_\n"
          for File::Find::Rule->new->maxdepth(1)->directory->in($dir);
      
        print "---\n";
      
        print "$_\n"
          for grep -d, glob "$dir/*"; ## use forward slashes, See File::Glob
      
      #~   chdir "..";
        rm_rf $dir;
      }
      __END__
      .
      |-- test.pl
      `-- tmp
          |-- a
          |   `-- b
          |       `-- c
          |           `-- d
          |-- ar
          |   `-- b
          |       `-- c
          |           `-- d
          `-- as
              `-- b
                  `-- c
                      `-- d
      
      13 directories, 1 file
      ---
      tmp
      tmp/a
      tmp/ar
      tmp/as
      ---
      tmp/a
      tmp/ar
      tmp/as
      

      或者使用 File::Find::Rule 前端 findrule

      $ findrule tmp -maxdepth ( 1 ) -directory
      tmp
      tmp/a
      tmp/ar
      tmp/as
      

      【讨论】:

        【解决方案8】:

        你可以使用 File::Find。例如:

        use File::Find ();
        
        File::Find::find(\&wanted, '.');
        
        sub wanted {
            if (-d) {    
                print "$File::Find::name\n";
            }
        }
        

        对于在'.' 下找到的每个文件,这将调用wanted 子例程。在子例程中,您可以使用-d 来检查目录。

        File::Find:find 下降到指定目录下的树中的所有子目录。

        【讨论】:

        • 抱歉,这不是readdirFile::Slurp::read_dir 的良好替代品。
        • 你能解释一下吗?我个人认为 read_dir 不是一个很好的替代品;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-05-31
        • 2015-02-01
        • 1970-01-01
        • 2012-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多