find folders with no further subfolders in perl
如何在给定路径中找到没有其他子文件夹的所有文件夹?它们可能包含文件,但没有其他文件夹。
例如,给定以下目录结构:
find(time)的输出应该如下:
1 2 3 4 5 6
| time/aa /bb /something /*
time/aa /bc /anything /*
time/aa /bc /everything /*
time/ab /cc /here /*
time/ab /cc /there /*
time/ab /cd /everywhere /* |
上面的
* 代表文件。
- 到目前为止你尝试了什么,你在哪里卡住了,你对你已经编写的代码有什么问题?我们不是一个"为我写一个解决方案"的网站。如果您不知道从哪里开始,可以查看 File::Find:search.cpan.org/~dom/perl-5.12.5/lib/File/Find.pm
任何时候你想写一个目录遍历器,总是使用标准的 File::Find 模块。在处理文件系统时,您必须能够处理奇怪的极端情况,而幼稚的实现很少这样做。
提供给回调的环境(在文档中命名为 wanted)具有三个变量,它们对您想要做的事情特别有用。
$File::Find::dir is the current directory name
$_ is the current filename within that directory
$File::Find::name is the complete pathname to the file
当我们找到一个不是 . 或 .. 的目录时,我们记录完整路径并删除它的父目录,我们现在知道它不能是叶目录。最后,任何剩余的记录路径都必须离开,因为 File::Find 中的 find 执行深度优先搜索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #! /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"$_\
" for sort keys %dirs; |
您可以对当前目录的子目录运行它
或使用完整路径
1 2 3 4 5 6 7
| $ leaf -dirs /tmp /time
/tmp /time/aa /bb /something
/tmp /time/aa /bc /anything
/tmp /time/aa /bc /everything
/tmp /time/ab /cc /here
/tmp /time/ab /cc /there
/tmp /time/ab /cd /everywhere |
或在同一调用中检测多个目录。
1 2 3 4 5 6 7 8 9
| $ mkdir -p /tmp /foo /bar /baz /quux
$ leaf -dirs /tmp /time /tmp /foo
/tmp /foo /bar /baz /quux
/tmp /time/aa /bb /something
/tmp /time/aa /bc /anything
/tmp /time/aa /bc /everything
/tmp /time/ab /cc /here
/tmp /time/ab /cc /there
/tmp /time/ab /cd /everywhere |
基本上,您打开根文件夹并使用以下步骤:
1 2
| sub child_dirs {
my ($directory) = @_; |
打开目录
从文件所在目录的文件中选择文件
1 2
| my @subdirs = grep {-d $_ and not m </\\ .\\ .?$>} map"$directory/$_", readdir $dir;
# ^-- directory and not . or .. ^-- use full name |
如果此类选定文件的列表包含元素,
3.1。然后递归到每个这样的目录,
3.2.否则此目录是 "leaf",它将被附加到输出文件中。
1 2 3 4 5 6
| if (@subdirs) {
return map {child_dirs ($_)} @subdirs;
} else {
return"$directory/*";
}
# OR: @subdirs ? map {child_dirs($_)} @subdirs :"$directory/*"; |
.
示例用法:
1
| say $_ for child_dirs("time"); # dir `time' has to be in current directory. |
我尝试了 readdir 的做事方式。然后我偶然发现了这个……
1 2 3
| use File::Find::Rule;
# find all the subdirectories of a given directory
my @subdirs = File::Find::Rule->directory->in( $directory ); |
我从这个输出中消除了与字符串的初始部分匹配并且没有某些叶条目的任何条目。
这个函数可以做到。只需使用您的初始路径调用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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\
"; }
} |