我正在做一个磁盘空间报告,它使用File::Find
来收集目录树中的累积大小。
我从File::Find
轻松获得的是目录名称。
e.g:
/path/to/user/username/subdir/anothersubdir/etc
我正在运行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 );
}
(是的,我知道这不是很好。)。
这样做的目的是当我stat
每个文件时,我将它的大小添加到当前节点和树中的每个父节点。
这足以产生:
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可视化 - 松散地基于D3 Zoomable Circle Pack
所以我的问题是这样 - 整理我的数据的最佳方法是什么,以便我可以累积(并且理想地非累积)大小调整信息,但是分层填充哈希。
我在思考'游标'方法(这次使用File::Spec
):
use File::Spec;
my $data;
my $cursor = \$data;
foreach my $element ( File::Spec -> splitdir ( $File::Find::dir ) ) {
$cursor -> {size} += $blocks * $block_size;
$cursor = $cursor -> {$element}
}
虽然......我并没有完全创建我正在寻找的数据结构,尤其是因为我们基本上必须通过哈希键来搜索进程的“滚动”部分。
有没有更好的方法来实现这个目标?
编辑 - 我已经拥有的更完整的例子:
#!/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";
}
}
是的 - 我知道这不便携,并做了一些有点讨厌的事情。我在这里做的部分工作就是努力改进。 (但目前它是一个基于Unix的homedir结构,所以没关系)。
答案 0 :(得分:3)
如果你自己进行dir扫描而不是使用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] // '.')));
答案 1 :(得分:0)
最后,我这样做了:
在us.states@data<-copy(us.states@data)[,test:=1L]
> names(us.states@data)
[1] "STATEFP" "STATENS" "AFFGEOID" "GEOID" "STUSPS" "NAME" "LSAD"
[8] "ALAND" "AWATER" "test"
想要的子File::Find
中:
collate_sizes
生成嵌套目录名称的哈希值。 (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} //= {};
}
子元素可能是多余的,但无论如何。
然后使用(使用name
)进行后处理:
JSON