2013-04-05 3 views
1

Я написал некоторый скрипт, который рекурсивно печатает содержимое каталога. Но он печатает предупреждение для каждой папки. Как это исправить?предупреждение дерева каталогов

образец папки:

Dev # Кд/TMP/тест
Dev # Ls -p -R
test2/
TestFile
testfile2

./test2:
testfile3
testfile4

мой код:

#!/usr/bin/perl 

use strict; 
use warnings; 

browseDir('/tmp/test'); 

sub browseDir { 
    my $path = shift; 
    opendir(my $dir, $path); 
    while (readdir($dir)) { 
     next if /^\.{1,2}$/; 
     if (-d "$path/$_") { 
      browseDir("$path/$_"); 
     } 
     print "$path/$_\n"; 
    } 
    closedir($dir); 
} 

и выход:

DEV # Perl /tmp/cotest.pl
/TMP/тест/test2/testfile3
/TMP/тест/test2/testfile4
Использование неинициализированным значения $ _ в конкатенации (.) или строки в /tmp/cotest.pl строке 16.
/TMP/тест/
/TMP/тест/TestFile
/TMP/тест/testfile2

+0

'Файл :: Spec' <- этот модуль имеет' no_upwards() 'func, чтобы скрыть все каталоги, такие как' .' или '..'. Пример: '@paths = Файл :: Spec-> no_upwards (@paths);' – gaussblurinc

+0

Вы должны поместить 'use 5.012;' в свою программу, поскольку вы используете 'while (readdir ($ dir)) {...}' который не работает в более ранних версиях. –

ответ

1

мая вы попробуйте ввести этот код:

#!/usr/bin/perl 

    use strict; 
    use warnings; 

    browseDir('/tmp'); 

    sub browseDir { 
     my $path = shift; 
     opendir(my $dir, $path); 
     while (readdir($dir)) { 
      next if /^\.{1,2}$/; 
      print "$path/$_\n"; 
      if (-d "$path/$_") { 
       browseDir("$path/$_"); 
      } 
     } 
     closedir($dir); 
    } 

Если у вас есть эта ошибка, ее, поскольку вы вызываете browseDir() перед использованием переменной $ _.

+0

спасибо, он работает. также, если я не использую переменную $ _, никакие предупреждения не печатаются. 'while (my $ s = readdir ($ dir)) {...}' – Suic

+0

Изменил ли ОП свой код? Я не могу найти разницы между твоей и его. – ikegami

+0

'print '$ path/$ _ \ n"; 'перемещено вверх – Suic

1

Вы поместите значение в $_ перед вызовом browseDir и вы ожидаете, что значение, которое должно присутствовать после вызова browseDir (разумное ожидание), но browseDir изменяет эту переменную.

Просто добавьте local $_; в browseDir, чтобы убедиться, что любые изменения к нему отменены до выхода sub.


Unrelated на свой вопрос, вот три других вопроса:

  • даже не проверяя минимальной ошибки!
  • Вы можете закончить работу с каталогами, которые будут перемещаться по глубокой директории.
  • Отфильтровывает файлы ".\n" и "..\n".

Fix:

#!/usr/bin/perl 

use strict; 
use warnings; 

browseDir('/tmp/test'); 

sub browseDir { 
    my $path = shift; 

    opendir(my $dh, $path) or die $!; 
    my @files = readdir($dh); 
    closedir($dh); 

    for (@files) { 
     next if /^\.{1,2}z/; 
     if (-d "$path/$_") { 
      browseDir("$path/$_"); 
     } 

     print "$path/$_\n"; 
    } 
} 

Наконец, почему не использовать вам модуль, как File::Find::Rule?

use File::Find::Rule qw(); 
print "$_\n" for File::Find::Rule->in('/tmp'); 

Примечание: Перед 5.12, while (readir($dh)) должно быть написано while (defined($_ = readdir($dh)))

+0

спасибо за ваши комментарии, я больше никогда не буду использовать $ _^ – Suic

+0

Фактически 'while (readdir ($ dir)) {...}' преобразуется в 'while (определяется ($ _ = readdir ($ dir))) {...} 'по [5.12] (http://perldoc.perl.org/perlfunc.html#readdir-DIRHANDLE). [commit] (http://perl5.git.perl.org/perl.git/commit/114c60ecb1f775ef1deb4fdc8fb8e3a6f343d13d) К сожалению, это никогда не превращало его в дельту. –

+0

@ Брэд Гилберт, о, действительно! Тогда проблема заключается в отсутствии локализации '$ _'. Рекурсивный вызов сбрасывает значение, которое родитель имеет в '$ _'. Обновленный ответ. – ikegami

1

Почему бы не использовать модуль File::Find? Он включен почти во все дистрибутивы Perl с Perl 5.x. Это не мой любимый модуль из-за грязного способа его работы, но он неплохо работает.

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

В File::Find имя файла хранится в $File::Find::name, а каталог для этого файла находится в $File::Find::dir. $_ является самим файлом и может использоваться для тестирования.

Вот основной способ, что вы хотите:

use strict; 
use warnings; 
use feature qw(say); 

use File::Find; 

my $directory = `/tmp/test`; 

find (\&wanted, $directory); 

sub wanted { 
    say $File::Find::Name; 
} 

Я предпочитаю, чтобы поставить свою wanted функции в моей find подпрограммы, так что они вместе. Это эквивалентно вышесказанному:

use strict; 
use warnings; 
use feature qw(say); 

use File::Find; 

my $directory = `/tmp/test`; 

find ( 
    sub { 
     say $File::Find::Name 
    }, 
    $directory, 
); 

Хорошее программирование говорит не печатать в подпрограммах. Вместо этого вы должны использовать подпрограмму для хранения и возврата ваших данных. К сожалению, find ничего не возвращает. Вы должны использовать глобальный массив, чтобы захватить список файлов, а затем распечатать их:

use strict; 
use warnings; 
use feature qw(say); 

use File::Find; 

my $directory = `/tmp/test`; 
my @directory_list; 

find ( 
    sub { 
     push @directory_list, $File::Find::Name 
    }, $directory); 

for my $file (@directory_list) { 
    say $file; 
} 

Или, если вы предпочитаете отдельную wanted подпрограмму:

use strict; 
use warnings; 
use feature qw(say); 

use File::Find; 

my $directory = `/tmp/test`; 
my @directory_list; 

find (\&wanted, $directory); 
sub wanted { 
    push @directory_list, $File::Find::Name; 
} 

for my $file (@directory_list) { 
    say $file; 
} 

Тот факт, что мой требуемая подпрограмма зависит от массива, который не является локальным для подпрограммы, беспокоит меня, поэтому я предпочитаю встраивать подпрограмму wanted внутри моего вызова find.

Одна вещь, которую вы можете сделать, - это использовать свою подпрограмму, чтобы отфильтровать то, что вы хотите. Допустим, вы заинтересованы только в JPG файлы:

use strict; 
use warnings; 
use feature qw(say); 

use File::Find; 

my $directory = `/tmp/test`; 
my @directory_list; 

find (\&wanted, $directory); 
sub wanted { 
    next unless /\.jpg$/i; #Skip everything that doesn't have .jpg suffix 
    push @directory_list, $File::Find::Name; 
} 

for my $file (@directory_list) { 
    say $file; 
} 

Обратите внимание, как разыскиваемый подпрограмма делает next на любой файл, я не хочу, прежде чем я толкаю его в мой @directory_list массив. Опять же, я предпочитаю вложение:

find (sub { 
    next unless /\.jpg$/i; #Skip everything that doesn't have .jpg suffix 
    push @directory_list, $File::Find::Name; 
} 

Я знаю, что это не совсем то, что вы просили, но я просто хотел, чтобы вы знали о Find::File модуля и познакомить вас с модулями Perl (если не уже знать о них), которые могут добавить много функциональности для Perl.

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