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ドキュメントで名前が付けられています) には、実行したいことに特に役立つ 3 つの変数があります。

$File::Find::dir現在のディレクトリ名です

$_そのディレクトリ内の現在のファイル名です

$File::Find::nameファイルへの完全なパス名です

.またはではないディレクトリを見つけたら..、完全なパスを記録し、その親を削除します。これは、リーフ ディレクトリではないことがわかっています。findFile::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/time/aa/bb/何か
/tmp/time/aa/bc/なんでも
/tmp/time/aa/bc/すべて
/tmp/time/ab/cc/こちら
/tmp/time/ab/cc/そこ
/tmp/time/ab/cd/どこでも

または、同じ呼び出しで複数のディレクトリを plumb します。

$ mkdir -p /tmp/foo/bar/baz/quux
$ リーフディレクトリ /tmp/time /tmp/foo
/tmp/foo/bar/baz/quux
/tmp/time/aa/bb/何か
/tmp/time/aa/bc/なんでも
/tmp/time/aa/bc/すべて
/tmp/time/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

This function will do it. Just call it with your initial path:

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 に答える