2012-05-15 40 views
2

为了遍历目录,我仍然使用很棒的Path::Class模块。我写了一段代码,但对输出的显示不满意。我的目录树输出不像tree命令的输出那样干净利落。 :-(树状命令显示目录树输出

我迄今为止代码:

use strict; 
use warnings; 
use Path::Class; 

my $dir = Path::Class::Dir->new('D:\dev\pl\testdir'); 

my $max_depth = $dir->traverse(sub { 
    my ($child, $cont, $depth) = @_; 
    return max($cont->($depth + 1), $depth); 
}, 0); 
sub max { my $max = 0; for (@_) { $max = $_ if $_ > $max } $max }; 

# Print header 
printf "%-43s|%s\n", " Name", " mtime"; 
printf "%-43s|%s\n", '-' x 43, '-' x 11; 

$dir->traverse(sub { 
    my ($child, $cont, $indent) = @_; 
    my $child_basename = $child->basename; 
    my $child_stat  = $child->stat(); 
    my $child_mtime = $child_stat->[9]; 
    $indent //= 0; 
    my $width = 40 - 3 * ($indent - 1); 

    #print "DEBUG: Scanning $child\n"; 

    if ($indent == 0) { 
    print "ROOT: ", $child, "\n"; 
    } 
    else { 
    if ($child->is_dir) { 
     print ' ' x ($indent - 1), '+- '; 
     printf "%-${width}s| %d", $child_basename . '/', $child_mtime; 
     print "\n"; 
    } else { 
     print ' ' x ($indent - 1), '|- '; 
     printf "%-${width}s| %d", $child_basename, $child_mtime; 
     print "\n"; 
    } 
    } 

    $cont->($indent + 1); 
}); 

而且我错误输出为:

Name          | mtime 
-------------------------------------------|----------- 
ROOT: D:\dev\pl\testdir 
+- Path-Class-0.25/      | 1337013211 
    |- Build.PL        | 1329360988 
    |- Changes        | 1329360988 
    |- dist.ini        | 1329360988 
    |- INSTALL        | 1329360988 
    +- lib/         | 1337013211 
     +- Path/        | 1337013211 
     +- Class/       | 1337013211 
      |- Dir.pm      | 1329360988 
      |- Entity.pm     | 1329360988 
      |- File.pm      | 1329360988 
     |- Class.pm      | 1329360988 
    |- LICENSE        | 1329360988 
    |- Makefile.PL       | 1329360988 
    |- MANIFEST        | 1329360988 
    |- META.yml        | 1329360988 
    |- README        | 1329360988 
    |- SIGNATURE       | 1329360988 
    +- t/         | 1337013211 
     |- 01-basic.t      | 1329360988 
     |- 02-foreign.t      | 1329360988 
     |- 03-filesystem.t     | 1329360988 
     |- 04-subclass.t      | 1329360988 
     |- 05-traverse.t      | 1329360988 
     |- author-critic.t     | 1329360988 

正确输出(也更好看)应该是:

Name          | mtime 
-------------------------------------------|----------- 
ROOT: D:\dev\pl\testdir 
+- Path-Class-0.25/      | 1337013211 
    |- Build.PL        | 1329360988 
    |- Changes        | 1329360988 
    |- dist.ini        | 1329360988 
    |- INSTALL        | 1329360988 
    +- lib/         | 1337013211 
    | +- Path/        | 1337013211 
    |  +- Class/       | 1337013211 
    |  | |- Dir.pm      | 1329360988 
    |  | |- Entity.pm     | 1329360988 
    |  | |- File.pm      | 1329360988 
    |  \- Class.pm      | 1329360988 
    |- LICENSE        | 1329360988 
    |- Makefile.PL       | 1329360988 
    |- MANIFEST        | 1329360988 
    |- META.yml        | 1329360988 
    |- README        | 1329360988 
    |- SIGNATURE       | 1329360988 
    \- t/         | 1337013211 
     |- 01-basic.t      | 1329360988 
     |- 02-foreign.t      | 1329360988 
     |- 03-filesystem.t     | 1329360988 
     |- 04-subclass.t      | 1329360988 
     |- 05-traverse.t      | 1329360988 
     \- author-critic.t     | 1329360988 

你能否请改善或纠正我的代码?

非常感谢您的帮助!

问候,
斯科特

+0

如果唯一的区别是每个目录的最后一个孩子应该拥有的,而不是垂直条通往上面有一条斜线(顺便说一句,你可以在问题中说),我认为你必须保存每个孩子的信息,直到所有的孩子都被处理完毕,然后把它们打印出来。 –

+0

哦,我发现你在这些行上没有出现多个'|'也有区别。 –

回答

3

下面我的代码并不花哨的解决方案,但它工作在你希望>>

#!/usr/bin/perl 

use strict; 
use warnings; 
use Path::Class; 

my $dir = Path::Class::Dir->new('D:\dev\pl\testdir'); 

my $max_depth = $dir->traverse(sub { 
    my ($child, $cont, $depth) = @_; 
    return max($cont->($depth + 1), $depth); 
}, 0); 

sub max { my $max = 0; for (@_) { $max = $_ if $_ > $max } $max }; 

my @output = (sprintf("%-43s|%s", " Name", " mtime"), 
       sprintf("%-43s|%s", '-' x 43, '-' x 11)); 

my @tree = (0, 0); 
my $last_indent = 0; 

$dir->traverse(sub { 
    my ($child, $cont, $indent) = @_; 
    my $child_basename = $child->basename; 
    my $child_stat  = $child->stat(); 
    my $child_mtime = $child_stat->[9]; 

    $indent = 1 if (!defined $indent); 
    my $width = 40 - 3 * ($indent - 1); 

    if ($last_indent != $indent) { 
    if ($last_indent > ($indent + 1)) { 
     for my $level (($indent + 1)..($last_indent - 1)) { 
     $output[$#output - $_] = 
      substr($output[$#output - $_], 0, 3 * ($level - 1)) . ' ' . 
      substr($output[$#output - $_], 3 * ($level - 1) + 1, 65535) 
      for (0..$tree[$level] - 1); 
     } 
     delete $tree[$_] for $indent..$last_indent; 
    } 
    $tree[$indent] = 0; 
    $last_indent = $indent; 
    } 

    if ($child->is_dir) { 
    push @output, sprintf("%s+- %-${width}s| %d", 
     ('| ' x ($indent - 1)), $child_basename . '/', $child_mtime); 
    $tree[$indent] = 0; 
    } 
    else { 
    push @output, sprintf("%s%s- %-${width}s| %d", ('| ' x ($indent - 1)), 
     ($child eq ($child->dir->children)[-1] ? '\\' : '|'), 
     $child_basename, $child_mtime); 
    $tree[$indent]++; 
    } 
    $tree[$_]++ for (1..$indent - 1); 

    $cont->($indent + 1); 
}); 

for my $level (1..$last_indent - 1) { 
    $output[$#output - $_] = 
    substr($output[$#output - $_], 0, 3 * ($level - 1)) . ' ' . 
    substr($output[$#output - $_], 3 * ($level - 1) + 1, 65535) 
     for (0..$tree[$level] - 1); 
} 

print "$_\n" for @output; 
+0

非常感谢!很棒。 :-) – Scottie

0
if ($child->is_dir) { 
    printf "%s+- %-${width}s| %d\n", 
     ('| ' x ($indent - 1)), 
     $child_basename . '/', 
     $child_mtime; 
} else { 
    printf "%s%s- %-${width}s| %d\n", 
     ('| ' x ($indent - 1)), 
     ($child eq ($child->dir->children)[-1] ? '\\' : '|'), 
     $child_basename, 
     $child_mtime; 
} 

此外,ASCII行导致眼癌。改为使用正确的box drawing characters

+0

谢谢!它效果更好但不理想 - 有些线条是不必要的。 – Scottie

+0

P.S. [不必要的行](http://i.imgur.com/V1FT6.png) – Scottie

+0

我没有耐心去编程这个变化,我今天很快就辞职了。我会给你提示修改的内容:对于第一个'%s'表达式,用'parent' $ indent时间查找树,然后在标量上下文中调用'children'。如果该目录有多个孩子,则输出一个“'| ''分段,如果只有一个孩子,输出一个''''分段。 – daxim