JSON数据结构中的文件路径

JSON数据结构中的文件路径,json,perl,Json,Perl,我正在做一个磁盘空间报告,它使用File::Find收集目录树中的累积大小 我(很容易)从File::Find中得到的是目录名 e、 g: 我正在运行File::Find以收集以下尺寸: /path/to/user/username 并构建目录和每个子目录的累积大小报告 我现在得到的是: while ( $dir_tree ) { %results{$dir_tree} += $blocks * $block_size; my @path_arr = split ( "/", $d

我正在做一个磁盘空间报告,它使用
File::Find
收集目录树中的累积大小

我(很容易)从
File::Find
中得到的是目录名

e、 g:

我正在运行
File::Find
以收集以下尺寸:

/path/to/user/username
并构建目录和每个子目录的累积大小报告

我现在得到的是:

while ( $dir_tree ) {
   %results{$dir_tree} += $blocks * $block_size;
   my @path_arr = split ( "/", $dir_tree ); 
   pop ( @path_arr );
   $dir_tree = join ( "/", @path_arr ); 
}
(是的,我知道这不太好。)

这样做的目的是,当我统计每个文件时,我将其大小添加到树中的当前节点和每个父节点

这足以产生:

username,300M
username/documents,150M
username/documents/excel,50M
username/documents/word,40M
username/work,70M
username/fish,50M,
username/some_other_stuff,30M
但我现在想把它转换成JSON,更像这样:

{ 
    "name" : "username",
    "size" : "307200",
    "children" : [
        { 
            "name" : "documents",
            "size" : "153750",
            "children" : [
                  { 
                      "name" : "excel",
                      "size" : "51200"
                   }, 
                   {
                       "name" : "word",
                       "size" : "81920"
                   }
             ]
         }
    ]
}
这是因为我打算对这个结构做一个D3可视化——松散地基于

所以我的问题是-什么是最整洁的方式来整理我的数据,这样我就可以有累积(理想情况下是非累积)的大小调整信息,但分层填充哈希

我在考虑“游标”方法(这次使用
File::Spec
):

虽然。。。这并不能完全创建我正在寻找的数据结构,尤其是因为我们基本上必须通过哈希键进行搜索,以完成过程中的“汇总”部分

有没有更好的方法来实现这一点

编辑-我已有的更完整示例:

#!/usr/bin/env perl

use strict;
use warnings;

use File::Find;
use Data::Dumper;

my $block_size = 1024;

sub collate_sizes {
    my ( $results_ref, $starting_path ) = @_;
    $starting_path =~ s,/\w+$,/,;
    if ( -f $File::Find::name ) {
        print "$File::Find::name isafile\n";
        my ($dev,   $ino,     $mode, $nlink, $uid,
            $gid,   $rdev,    $size, $atime, $mtime,
            $ctime, $blksize, $blocks
        ) = stat($File::Find::name);

        my $dir_tree = $File::Find::dir;
        $dir_tree =~ s|^$starting_path||g;
        while ($dir_tree) {
            print "Updating $dir_tree\n";
            $$results_ref{$dir_tree} += $blocks * $block_size;
            my @path_arr = split( "/", $dir_tree );
            pop(@path_arr);
            $dir_tree = join( "/", @path_arr );
        }
    }
}

my @users = qw ( user1 user2 );

foreach my $user (@users) {
    my $path = "/home/$user";
    print $path;
    my %results;
    File::Find::find(
        {   wanted   => sub { \&collate_sizes( \%results, $path ) },
            no_chdir => 1
        },
        $path
    );
    print Dumper \%results;

    #would print this to a file in the homedir - to STDOUT for convenience
    foreach my $key ( sort { $results{$b} <=> $results{$a} } keys %results ) {
       print "$key => $results{$key}\n";
    }
}

#/usr/bin/env perl
严格使用;
使用警告;
使用File::Find;
使用数据::转储程序;
我的$block_size=1024;
子校对尺寸{
我的($results\u ref,$starting\u path)=@;
$starting_path=~s,/\w+$,/,;
如果(-f$File::Find::name){
打印“$File::Find::name isafile\n”;
我的($dev、$ino、$mode、$nlink、$uid、,
$gid、$rdev、$size、$atime、$mtime、,
$ctime、$blksize、$blocks
)=stat($File::Find::name);
my$dir_tree=$File::Find::dir;
$dir|u tree=~s|^$starting|u path|g;
while($dir_tree){
打印“更新$dir\u树\n”;
$$results\u ref{$dir\u tree}+=$blocks*$block\u size;
my@path\u arr=split(“/”,$dir\u tree);
pop(@path_arr);
$dir\u tree=join(“/”,@path\u arr);
}
}
}
my@users=qw(user1 user2);
foreach my$user(@users){
my$path=“/home/$user”;
打印$path;
我的%结果;
文件::查找::查找(
{wanted=>sub{\&collate\u大小(\%results,$path)},
否\u chdir=>1
},
$path
);
打印转储程序\%结果;
#为了方便起见,会将其打印到homedir-to标准输出中的文件中
foreach my$键(排序{$results{$b}$results{$a}}键%results){
打印“$key=>$results{$key}\n”;
}
}

是的,我知道这不是便携式的,而且会做一些有点讨厌的事情。我在这里所做的部分工作是努力改进这一点。(但目前它是一个基于Unix的homedir结构,所以这很好)

最后,我是这样做的:

{ 
    "name" : "username",
    "size" : "307200",
    "children" : [
        { 
            "name" : "documents",
            "size" : "153750",
            "children" : [
                  { 
                      "name" : "excel",
                      "size" : "51200"
                   }, 
                   {
                       "name" : "word",
                       "size" : "81920"
                   }
             ]
         }
    ]
}
文件::查找
通缉子
核对大小

my $cursor = $data;
foreach my $element (
    File::Spec->splitdir( $File::Find::dir =~ s/^$starting_path//r ) )
{
    $cursor->{$element}->{name} = $element;
    $cursor->{$element}->{size} += $blocks * $block_size;
    $cursor = $cursor->{$element}->{children} //= {};
}
生成嵌套目录名的哈希。(名子元素可能是多余的,但无论如何)

然后使用(使用
JSON
)对其进行后期处理:


如果您自己进行目录扫描,而不是使用File::Find,那么自然会得到正确的结构

sub _scan {
   my ($qfn, $fn) = @_;
   my $node = { name => $fn };

   lstat($qfn)
      or die $!;

   my $size   = -s _;
   my $is_dir = -d _;

   if ($is_dir) {
      my @child_fns = do {
         opendir(my $dh, $qfn)
            or die $!;

         grep !/^\.\.?\z/, readdir($dh);
      };

      my @children;
      for my $child_fn (@child_fns) {
         my $child_node = _scan("$qfn/$child_fn", $child_fn);
         $size += $child_node->{size};
         push @children, $child_node;
      }

      $node->{children} = \@children;
   }

   $node->{size} = $size;
   return $node;
}
代码的其余部分:

#!/usr/bin/perl

use strict;
use warnings;    
no warnings 'recursion';

use File::Basename qw( basename );
use JSON           qw( encode_json );

...    

sub scan { _scan($_[0], basename($_[0])) }

print(encode_json(scan($ARGV[0] // '.')));

你能添加一个完整的例子,我可以懒洋洋地复制/粘贴吗?好的,请容忍我。我得把我的脚本缩小一点。好的。添加了一个最小的示例。(它省略了很多垃圾,比如一些单元格式和不同的摘要输出)。看起来是一种非常直接的方法。是的。但是导出到JSON的效果不太好。我本来打算提出一些类似的建议,但是没有光标,按照您已经拥有的方式进行处理,并迭代最后的结构,为JSON处理创建第二个结构。但是还没来得及写出来。
-s>中的
有打字错误
-d
@simbabque,我想你的意思是我应该用
$\u
而不是
\u
,但你错了
\uu
是由
stat
lstat
填充的句柄。这样,我只需执行一次系统调用,只需检查一次错误。更改为停止跟踪符号链接
sub _scan {
   my ($qfn, $fn) = @_;
   my $node = { name => $fn };

   lstat($qfn)
      or die $!;

   my $size   = -s _;
   my $is_dir = -d _;

   if ($is_dir) {
      my @child_fns = do {
         opendir(my $dh, $qfn)
            or die $!;

         grep !/^\.\.?\z/, readdir($dh);
      };

      my @children;
      for my $child_fn (@child_fns) {
         my $child_node = _scan("$qfn/$child_fn", $child_fn);
         $size += $child_node->{size};
         push @children, $child_node;
      }

      $node->{children} = \@children;
   }

   $node->{size} = $size;
   return $node;
}
#!/usr/bin/perl

use strict;
use warnings;    
no warnings 'recursion';

use File::Basename qw( basename );
use JSON           qw( encode_json );

...    

sub scan { _scan($_[0], basename($_[0])) }

print(encode_json(scan($ARGV[0] // '.')));