2016-04-24 3 views
1

Я пытаюсь получить все файлы и каталоги из заданного каталога, но я не могу указать, какой тип (файл/каталог). Ничего не печатается. Что я делаю неправильно и как его решить. Вот код:Печать файлов и подкаталогов данного каталога

sub DoSearch { 
    my $currNode = shift; 
    my $currentDir = opendir (my $dirHandler, $currNode->rootDirectory) or die $!; 

    while (my $node = readdir($dirHandler)) { 
     if ($node eq '.' or $node eq '..') { 
      next; 
     } 

     print "File: " . $node . "\n" if -f $node; 
     print "Directory " . $node . "\n" if -d $node; 
    } 

    closedir($dirHandler); 
} 

ответ

5

readdir возвращает имя узла без какой-либо информации о пути. Операторы тестирования файлов будут выглядеть в текущем рабочем каталоге, если не указан путь, и потому, что текущий каталог не $currNode->rootDirectory они не будут найдены

Я предлагаю вам использовать rel2abs из File::Spec::Functions основных модуля объединить узел имя с указанием пути. Вы можете использовать конкатенацию строк, но библиотечная функция позаботится об угловых случаях, например о том, заканчивается ли каталог с помощью косой черты

Также стоит отметить, что идентификаторы Perl чаще всего находятся в snake_case, и люди, знакомые с этим языком, за неиспользование заглавных букв. Они должны особенно избегать для первого символа идентификатора, как имена, как, зарезервированные для глобал как имена пакетов

Я думаю, что ваша подпрограмма должна выглядеть следующим образом

use File::Spec::Functions 'rel2abs'; 

sub do_search { 
    my ($curr_node) = @_; 
    my $dir   = $curr_node->rootDirectory; 

    opendir my $dh, $dir or die qq{Unable to open directory "$dir": $!}; 

    while (my $node = readdir $dh) { 
     next if $node eq '.' or $node eq '..'; 

     my $fullname = rel2abs($node, $dir); 

     print "File:  $node\n" if -f $fullname; 
     print "Directory $node\n" if -d $fullname; 
    } 
} 

Альтернативный метод состоит в установке текущий рабочий каталог в считываемом каталоге. Таким образом, нет необходимости манипулировать пути к файлам, но вам нужно будет сохранить и восстановить исходный рабочий каталог до и после изменения его

Модуль Cwd ядра обеспечивает getcwd и ваш код будет выглядеть следующим образом

use Cwd 'getcwd'; 

sub do_search { 
    my ($curr_node) = @_; 

    my $cwd = getcwd; 
    chdir $curr_node->rootDirectory or die $!; 

    opendir my $dh, '.' or die $!; 

    while (my $node = readdir $dh) { 
     next if $node eq '.' or $node eq '..'; 

     print "File: \n" if -f $node; 
     print "Directory $node\n" if -d $node; 
    } 

    chdir $cwd or die $!; 
} 
+0

Благодарим вас за предоставленные решения. Первая решила мою проблему :) –

1

Используйте этот модуль CPAN, чтобы рекурсивно получить все файлы и подкаталоги. только

use File::Find;   

    find(\&getFile, $dir); 
    my @fileList; 

    sub getFile{ 
     print $File::Find::name."\n"; 
     # Below lines will print only file name. 
     #if ($File::Find::name =~ /.*\/(.*)/ && $1 =~ /\./){ 
      #push @fileList, $File::Find::name."\n"; 
     } 
+0

Это действительно не разрешает проблему OP – Borodin

+1

Я думаю, что сначала мы должны использовать модули CPAN, а не писать собственный код, потому что он уже оптимизирован и если он решает проблему. – AbhiNickz

+0

Это очень неуместно. Вы можете думать, что OP должен использовать Python вместо этого, но это не делает полезное решение Python. Вы не помогли OP понять, что не так с его собственным кодом, и ваше решение повторяется, когда требуется нерекурсивное решение , Ваш результат даже не похож на исходный код, и в вашем ответе есть неиспользуемый массив и несколько грязных разделов с комментариями. Это плохое решение – Borodin

1

Уже ответил, но иногда удобно не заботиться о деталях реализации, и вы можете использовать некоторые модули CPAN для скрытия таких деталей.

Один из них - прекрасный модуль Path::Tiny.

Ваш код может быть:

use 5.014;   #strict + feature 'say' + ... 
use warnings; 
use Path::Tiny; 

do_search($_) for @ARGV; 

sub do_search { 
     my $curr_node = path(shift); 
     for my $node ($curr_node->children) { 
       say "Directory : $node" if -d $node; 
       say "Plain File : $node" if -f $node; 
     } 
} 

Метод children исключает . и .. автоматически.

Вам также необходимо понять, что тест -f действителен только для реальных files. Таким образом, приведенный выше код исключает, например, symlinks (чьи точки относятся к реальным файлам) или файлы FIFO и т. Д. Такие «файлы» можно было бы обычно открывать и читать как обычные файлы, поэтому иногда вместо -f удобно используйте тест -e && ! -d (например, существует, но не каталог).

У Path::Tiny есть некоторые способы для этого, например. вы могли бы написать

 for my $node ($curr_node->children) { 
       print "Directory : $node\n" if $node->is_dir; 
       print "File  : $node\n" if $node->is_file; 
     } 

метод is_file обычно DWIM - например, делает: -e && ! -d.

Использование Path::Tiny вы также можете легко расширить функцию ходить все дерево с помощью метода iterator:

use 5.014; 
use warnings; 
use Path::Tiny; 

do_search($_) for @ARGV; 

sub do_search { 

    #maybe you need some error-checking here for the existence of the argument or like... 

    my $iterator = path(shift)->iterator({recurse => 1}); 
    while(my $node = $iterator->()) { 
     say "Directory : ", $node->absolute if $node->is_dir; 
     say "File  : ", $node->absolute if $node->is_file; 
    } 
} 

Вышеприведенных печатает тип для всех файлов и каталогов рекурсивной вниз от данного аргумента ...

И так далее ... Path::Tiny действительно стоит установить.

Смежные вопросы