3

如何在给定路径中找到没有其他子文件夹的所有文件夹?它们可能包含文件,但没有其他文件夹。

例如,给定以下目录结构:

time/aa/
time/aa/bb
time/aa/bb/something/*
time/aa/bc
time/aa/bc/anything/*
time/aa/bc/everything/*
time/ab/
time/ab/cc
time/ab/cc/here/*
time/ab/cc/there/*
time/ab/cd
time/ab/cd/everywhere/*
time/ac/

的输出find(time)应如下所示:

time/aa/bb/something/*
time/aa/bc/anything/*
time/aa/bc/everything/*
time/ab/cc/here/*
time/ab/cc/there/*
time/ab/cd/everywhere/*

*上面代表文件。

4

4 回答 4

8

任何时候你想写一个目录遍历器,总是使用标准的File::Find模块。在处理文件系统时,您必须能够处理奇怪的极端情况,而幼稚的实现很少这样做。

提供给回调的环境(在文档中命名wanted)具有三个变量,它们对您想要做的事情特别有用。

$File::Find::dir是当前目录名

$_是该目录中的当前文件名

$File::Find::name是文件的完整路径名

当我们找到一个不是.or的目录时..,我们记录完整的路径并删除它的父目录,我们现在知道它不能是叶子目录。最后,任何剩余的记录路径都必须留下,因为find在 File::Find 中执行深度优先搜索

#! /usr/bin/env perl

use strict;
use warnings;

use File::Find;

@ARGV = (".") unless @ARGV;

my %dirs;
sub wanted {
  return unless -d && !/^\.\.?\z/;
  ++$dirs{$File::Find::name};
  delete $dirs{$File::Find::dir};
}

find \&wanted, @ARGV;
print "$_\n" for sort keys %dirs;

您可以针对当前目录的子目录运行它

$ 叶目录时间
时间/aa/bb/某事
时间/aa/bc/任何东西
时间/aa/bc/一切
时间/ab/cc/这里
时间/ab/cc/那里
时间/ab/cd/无处不在

或使用完整路径

$叶目录/tmp/时间
/tmp/时间/aa/bb/某事
/tmp/时间/aa/bc/任何东西
/tmp/时间/aa/bc/一切
/tmp/时间/ab/cc/这里
/tmp/time/ab/cc/那里
/tmp/time/ab/cd/无处不在

或在同一调用中探测多个目录。

$ mkdir -p /tmp/foo/bar/baz/quux
$ 叶目录 /tmp/time /tmp/foo
/tmp/foo/bar/baz/quux
/tmp/时间/aa/bb/某事
/tmp/时间/aa/bc/任何东西
/tmp/时间/aa/bc/一切
/tmp/时间/ab/cc/这里
/tmp/time/ab/cc/那里
/tmp/time/ab/cd/无处不在
于 2012-12-01T12:16:47.230 回答
1

基本上,您打开根文件夹并使用以下过程:

sub child_dirs {
    my ($directory) = @_;
  1. 打开目录

    opendir my $dir, $directory or die $!;
    
  2. 从此目录中的文件中选择文件,其中文件是目录

    my @subdirs = grep {-d $_ and not m</\.\.?$>} map "$directory/$_", readdir $dir;
    #                  ^-- directory and not . or ..  ^-- use full name
    
  3. 如果此类选定文件的列表包含元素,则
    3.1。然后递归到每个这样的目录,
    3.2。否则这个目录是一个“叶子”,它将被附加到输出文件中。

    if (@subdirs) {
       return map {child_dirs($_)} @subdirs;
    } else {
       return "$directory/*";
    }
    # OR: @subdirs ? map {child_dirs($_)} @subdirs : "$directory/*";
    

.

}

示例用法:

say $_ for child_dirs("time"); # dir `time' has to be in current directory.
于 2012-12-01T10:41:48.830 回答
0

这个功能会做到的。只需使用您的初始路径调用它:

sub isChild {

  my $folder = shift;
  my $isChild = 1;

    opendir(my $dh, $folder) || die "can't opendir $folder: $!";
    while (readdir($dh)) {
      next if (/^\.{1,2}$/); # skip . and ..
      if (-d "$folder/$_") {
        $isChild = 0;
        isChild("$folder/$_");
      }
    }

    closedir $dh;

    if ($isChild) { print "$folder\n"; }

}
于 2012-12-01T10:40:32.667 回答
0

我尝试了 readdir 的做事方式。然后我偶然发现了这个......

  use File::Find::Rule;
  # find all the subdirectories of a given directory
  my @subdirs = File::Find::Rule->directory->in( $directory );

我从该输出中消除了与字符串的初始部分匹配且没有某些叶条目的任何条目。

于 2012-12-03T01:21:08.447 回答