2010-03-19 6 views
10

Chcę odczytać katalog rekurencyjnie, aby wydrukować strukturę danych na stronie HTML z szablonem :: Toolkit. Wiem jednak, jak zapisać ścieżki i pliki w formularzu, który można odczytać jako łatwy.Jak mogę rekurencyjnie odczytać katalogi w Perlu?

Mój pomysł zaczął się ten

sub list_dirs{ 

    my ($rootPath) = @_; 
    my (@paths); 

    $rootPath .= '/' if($rootPath !~ /\/$/); 

    for my $eachFile (glob($path.'*')) 
    { 

     if(-d $eachFile) 
     { 
       push (@paths, $eachFile); 

       &list_dirs($eachFile); 
      } 
      else 
      { 
       push (@files, $eachFile); 
      } 
    } 

return @paths; 
} 

Jak mogę rozwiązać ten problem?

Odpowiedz

18

To powinno załatwić sprawę

use strict; 
use warnings; 
use File::Find qw(finddepth); 
my @files; 
finddepth(sub { 
     return if($_ eq '.' || $_ eq '..'); 
     push @files, $File::Find::name; 
}, '/my/dir/to/search'); 
+0

, więc glob faktycznie nie obsługuje rekursywnej listy plików, prawda? –

+0

To prawda, dostanie tylko pliki z podanego katalogu. – Htbaa

8

Należy zawsze stosować ścisłe i ostrzeżenia, aby pomóc w debugowaniu kodu. Perl ostrzegłby Cię, na przykład, że @files nie jest zadeklarowany. Ale prawdziwym problemem z twoją funkcją jest deklarowanie zmiennej leksykalnej @paths dla każdego wywołania rekursywnego na list_dirs i nie wypychanie wartości zwracanej z powrotem po etapie rekursji.

push @paths, list_dir($eachFile) 

Jeśli nie chcesz instalować dodatkowe moduły, następujące rozwiązanie powinno prawdopodobnie pomóc:

use strict; 
use warnings; 
use File::Find qw(find); 

sub list_dirs { 
     my @dirs = @_; 
     my @files; 
     find({ wanted => sub { push @files, $_ } , no_chdir => 1 }, @dirs); 
     return @files; 
} 
+0

z tej rutyny dostaję tylko jeden wynik, a to jest katalog daję rutynę, aby rozpocząć. – Przemek

+0

Czy możesz pokazać mi kod, którego używasz do badania tego? Działa tutaj dobrze. Może przetestuj go za pomocą "użyj Dumper, wydrukuj Dumper list_dirs"/home "". – mdom

3

myślę, że masz problem w następującej linii w kodzie

for my $eachFile (glob($path.'*')) 

Zmieniasz zmienną ścieżki $ na $ rootpath.

Zapisuje ścieżkę poprawnie.

5

Odpowiedź przez mdom wyjaśnia jak początkowy próba pobłądził. Sugerowałbym także, abyś rozważył bardziej przyjazne alternatywy dla File::Find. CPAN ma kilka opcji. Tu jest jeden.

use strict; 
use warnings; 
use File::Find::Rule; 
my @paths = File::Find::Rule->in(@ARGV); 

patrz także tutaj:

Tutaj znajduje się przepis na twoje rekurencyjne rozwiązanie. Rzeczy do zapamiętania: use strict; use warnings; oraz użycie bloku zakresu w celu utworzenia zmiennej statycznej dla podprogramu.

use strict; 
use warnings; 

print $_, "\n" for dir_listing(@ARGV); 

{ 
    my @paths; 
    sub dir_listing { 
     my ($root) = @_; 
     $root .= '/' unless $root =~ /\/$/; 
     for my $f (glob "$root*"){ 
      push @paths, $f; 
      dir_listing($f) if -d $f; 
     } 
     return @paths; 
    } 
} 
1

mogę użyć tego skryptu, aby usunąć ukryte pliki (utworzone przez Mac OS X) z mojego usb pendrive, gdzie zwykle go używać do słuchania muzyki w samochodzie, a każdy plik kończąc”.mp3" , nawet kiedy zaczyna się od "._", pojawi się na liście car audio.

#!/bin/perl 

use strict; 
use warnings; 

use File::Find qw(find); 

sub list_dirs { 
     my @dirs = @_; 
     my @files; 
     find({ wanted => sub { push @files, $_ } , no_chdir => 1 }, @dirs); 
     return @files; 
} 

if (! @ARGV || !$ARGV[0]) { 
    print "** Invalid dir!\n"; 
    exit ; 
} 


if ($ARGV[0] !~ /\/Volumes\/\w/s) { 
    print "** Dir should be at /Volume/... > $ARGV[0]\n"; 
    exit ; 
} 

my @paths = list_dirs($ARGV[0]) ; 

foreach my $file (@paths) { 
    my ($filename) = ($file =~ /([^\\\/]+)$/s) ; 

    if ($filename =~ /^\._/s) { 
    unlink $file ; 
    print "rm> $file\n" ; 
    } 
} 
0

można użyć tej metody jako rekurencyjnego wyszukiwania plików, które oddzielają określonych typów plików,

my @files; 
push @files, list_dir($outputDir); 

sub list_dir { 
     my @dirs = @_; 
     my @files; 
     find({ wanted => sub { push @files, glob "\"$_/*.txt\"" } , no_chdir => 1 }, @dirs); 
     return @files; 
}