【问题标题】:Display directory tree output like tree command像树命令一样显示目录树输出
【发布时间】:2012-05-15 18:28:47
【问题描述】:

我仍然使用很棒的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

能否改进或更正我的代码?

非常感谢您的帮助!

问候,
斯科蒂

【问题讨论】:

  • 如果唯一的区别是每个目录的最后一个子目录应该有一个斜杠而不是一个垂直条进入它(顺便说一句,你可以在问题中说),我想你会必须保存每个孩子的信息,直到所有孩子都处理完毕,然后打印出来。
  • 哦,我看到你也有不同之处,多个| 没有出现在这些行上。

标签: perl tree directory traversal tree-traversal


【解决方案1】:

我下面的代码不是花哨的解决方案,但它可以按你的意愿工作 >>

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

【讨论】:

    【解决方案2】:
    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

    【讨论】:

    • 谢谢!它工作得更好但并不理想 - 有些行是不必要的。
    • 我没有耐心对这个变化进行编程,我今天很快就退出了。我会提示你修改什么:对于第一个 %s 表达式,使用 parent $indent 次查找树,然后在标量上下文中调用 children。如果该目录有多个子目录,则输出'| ' 段,如果只有一个子目录,则输出' ' 段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-19
    • 1970-01-01
    • 1970-01-01
    • 2016-08-14
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多