Как читать каталоги и подкаталоги, не зная имени каталога в perl?



Привет я хочу читать каталоги и подкаталоги, не зная имени каталога. Текущий каталог-это "D:/Temp". 'Temp' имеет подкаталоги типа 'A1','A2'. Опять же 'A1' имеет подкаталоги типа 'B1','B2'. Опять же 'B1' имеет подкаталоги типа 'C1','C2'. Perl script не знает этих каталогов. Поэтому он должен сначала найти каталог, а затем прочитать один файл за раз в dir 'C1', как только все файлы будут прочитаны в 'C1', он должен измениться на dir 'C2'. Я пробовал с кодом ниже здесь я не хочу читать все файлы в массиве (@files), но нужен один файл одновременно. В массиве @dir элементы должны быть в виде паров.



$dir[0] = "D:/Temp/A1/B1/C1"
$dir[1] = "D:/Temp/A1/B1/C2"
$dir[2] = "D:/Temp/A1/B2/C1"


Ниже приведен код, который я пробовал.



    use strict;
use File::Find::Rule;
use Data::Dumper;

my $dir = "D:/Temp";
my @dir = File::Find::Rule->directory->in($dir);
print Dumper (@dir);
my $readDir = $dir[3];
opendir ( DIR, $readDir ) || die "Error in opening dir $readDirn";
my @files = grep { !/^..?$/ } readdir DIR;
print STDERR "files: @files nn";

for my $fil (@files) {
open (F, "<$fil");
read (F, my $data);
close (F);
print "$data";
}
559   5  

5 ответов:

Ваша цель: найти абсолютные пути к тем каталогам, которые сами по себе не имеют дочерних каталогов.

Я буду называть эти каталоги интересующими терминальными каталогами. Вот прототип функции, которая, как я полагаю, обеспечивает удобство, которое вы ищете. Функция возвращает свой результат в виде списка.

my @list = find_terminal_directories($full_or_partial_path);

И вот реализация find_terminal_directories(). Обратите внимание, что эта реализация не требует использования любого глобального переменные . Также обратите внимание на использование частной вспомогательной функции, которая называется рекурсивно.

В моей системе Windows 7, для входного каталога C:/Perl/lib/Test , я получаю вывод:

== List of Terminal Folders ==
c:/Perl/lib/Test/Builder/IO
c:/Perl/lib/Test/Builder/Tester
c:/Perl/lib/Test/Perl/Critic

== List of Files in each Terminal Folder: ==
c:/Perl/lib/Test/Builder/IO/Scalar.pm
c:/Perl/lib/Test/Builder/Tester/Color.pm
c:/Perl/lib/Test/Perl/Critic/Policy.pm

Реализация

#!/usr/bin/env perl

use strict;
use warnings;
use Cwd qw(abs_path getcwd);

my @dir_list = find_terminal_directories("C:/Perl/lib/Test");
print "== List of Terminal Directories ==\n";
print join("\n", @dir_list), "\n";

print "\n== List of Files in each Terminal Directory: ==\n";
for my $dir (@dir_list) {
    for my $file (<"$dir/*">) {
        print "$file\n";
        open my $fh, '<', $file or die $!;
        my $data = <$fh>;  # slurp entire file contents into $data
        close $fh;

        # Now, do something with $data !
    }
}

sub find_terminal_directories {
    my $rootdir = shift;

    my @wanted;
    my $cwd = getcwd();
    chdir $rootdir;
    find_terminal_directories_helper(".", \@wanted);
    chdir $cwd;
    return @wanted;
}

sub find_terminal_directories_helper {
    my ($dir, $wanted) = @_;
    return if ! -d $dir;
    opendir(my $dh, $dir) or die "open directory error!";
    my $count = 0;
    foreach my $child (readdir($dh)) {
        my $abs_child = abs_path($child);
        next if (! -d $child || $child eq "." || $child eq "..");
        ++$count;
        chdir $child;
        find_terminal_directories_helper($abs_child, $wanted);  # recursion!
        chdir "..";
    }
    push @$wanted, abs_path($dir) if ! $count;  # no sub-directories found!
}
use File::Find;

use strict;
use warnings;

my @dirs;
my %has_children;

find(sub {
    if (-d) {
        push @dirs, $File::Find::name;
        $has_children{$File::Find::dir} = 1;
    }
}, 'D:/Temp');

my @ends = grep {! $has_children{$_}} @dirs;

print "$_\n" for (@ends);

Возможно, будет полезно следующее:

use strict;
use warnings;
use File::Find::Rule;

my $dir = "D:/Temp";
local $/;

my @dirs =
  sort File::Find::Rule->exec( sub { File::Find::Rule->directory->in($_) == 1 }
  )->directory->in($dir);

for my $dir (@dirs) {
    for my $file (<"$dir/*">) {
        open my $fh, '<', $file or die $!;
        my $data = <$fh>;
        close $fh;
        print $data;
    }
}
  • local $/; позволяет нам слить содержимое файла в переменную. Удалите его, если вы хотите прочитать только первую строку.
  • sub в exec() используется для передачи только тех dir, которые не содержат dir
  • sort используется для упорядочивания этих дир в нужном вам порядке
  • файл Глоб <"$dir/*"> используется для получения файлов в каждом dir

Edit : модифицировали код для поиска только " каталоги терминалов."Спасибо DavidRR за это уточнение спецификации.

Я бы использовал File::Find

Пример сценария:

#!/usr/bin/perl

use strict; 
use warnings;

use File::Find;

my $dir = "/home/chris";

find(\&wanted, $dir);

sub wanted {
    print "dir: $File::Find::dir\n";
    print "file in dir: $_\n";
    print "complete path to file: $File::Find::name\n";
}

Выходы:

$ test.pl
dir: /home/chris/test_dir
file in dir: test_dir2
complete path to file: /home/chris/test_dir/test_dir2
dir: /home/chris/test_dir/test_dir2
file in dir: foo.txt
complete path to file: /home/chris/test_dir/test_dir2/foo.txt
...

Используя backticks, запишите субдиры и файлы в файл filelist:

`ls -R $dir > filelist`

Comments

    Ничего не найдено.