2010-03-19 64 views
10

Tôi muốn đọc một thư mục đệ quy để in cấu trúc dữ liệu trong một trang HTML với Template :: Toolkit. Nhưng tôi đang treo trong cách lưu các Đường dẫn và Tệp trong một biểu mẫu có thể đọc dễ dàng của chúng tôi.Làm thế nào tôi có thể đệ quy đọc các thư mục trong Perl?

Ý tưởng của tôi bắt đầu như thế này

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; 
} 

Làm thế nào tôi có thể giải quyết vấn đề này?

Trả lời

18

này nên làm các trick

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

vì vậy, glob thực sự không hỗ trợ danh sách đệ quy các tệp, phải không? –

+0

đúng, nó sẽ chỉ nhận các tệp từ thư mục đã cho. – Htbaa

8

Bạn nên luôn sử dụng nghiêm ngặt và cảnh báo để giúp bạn gỡ lỗi mã của mình. Perl đã cảnh báo bạn ví dụ rằng @files không được khai báo. Nhưng vấn đề thực sự với chức năng của bạn là bạn khai báo một biến lexical @paths trên mọi cuộc gọi đệ quy đến list_dirs và không đẩy giá trị trả về sau bước đệ quy.

push @paths, list_dir($eachFile) 

Nếu bạn không muốn cài đặt các module bổ sung, giải pháp sau đây có thể sẽ giúp bạn:

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

với thói quen này, tôi chỉ nhận được một kết quả, và điều này là thư mục tôi cung cấp cho các thói quen để bắt đầu. – Przemek

+0

Bạn có thể chỉ cho tôi mã bạn sử dụng để kiểm tra điều này không? Nó hoạt động tốt ở đây. Có thể thử nghiệm nó bằng "sử dụng Dumper; in Dumper list_dirs '/ home'". – mdom

3

Tôi nghĩ bạn có vấn đề trong các dòng sau trong mã của bạn

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

Bạn thay đổi biến $ path thành $ rootpath.

Nó sẽ lưu trữ đường dẫn chính xác.

5

Câu trả lời bởi mdom giải thích cách nỗ lực ban đầu của bạn đã đi lạc lối. Tôi cũng khuyên bạn nên xem xét các lựa chọn thay thế thân thiện hơn cho File::Find. CPAN có một số tùy chọn. Đây là một.

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

Xem thêm ở đây:

  • SO answer cung cấp CPAN thay thế cho File::Find.

  • SO question trên các trình vòng lặp thư mục.

Và đây là ghi đè giải pháp đệ quy của bạn. Những điều cần lưu ý: use strict; use warnings; và việc sử dụng một khối phạm vi để tạo một biến tĩnh cho chương trình con.

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

tôi sử dụng kịch bản này để loại bỏ các tập tin ẩn (tạo ra bởi Mac OS X) từ pendrive USB của tôi, nơi tôi thường sử dụng nó để nghe nhạc trong xe, và bất kỳ tập tin kết thúc bằng" .mp3" , thậm chí khi bắt đầu bằng "._", sẽ được liệt kê trong danh sách âm thanh xe hơi.

#!/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

bạn có thể sử dụng phương pháp này như tìm kiếm tập tin đệ quy mà phân biệt các loại tập tin cụ thể,

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; 
} 
Các vấn đề liên quan