2015-09-03 22 views
6

Tôi đang tạo báo cáo dung lượng đĩa sử dụng File::Find để thu thập kích thước tích lũy trong cây thư mục.Đường dẫn tệp vào cấu trúc dữ liệu JSON

Điều tôi nhận được (dễ dàng) từ File::Find là tên thư mục.

ví dụ .:

/path/to/user/username/subdir/anothersubdir/etc 

Tôi đang chạy File::Find để thu thập kích thước bên dưới:

/path/to/user/username 

Và xây dựng một báo cáo kích thước tích lũy của thư mục và mỗi người trong số các thư mục con.

Những gì tôi hiện đã có là: (. Và vâng, tôi biết đó không phải là rất đẹp)

while ($dir_tree) { 
    %results{$dir_tree} += $blocks * $block_size; 
    my @path_arr = split ("/", $dir_tree); 
    pop (@path_arr); 
    $dir_tree = join ("/", @path_arr); 
} 

.

Mục đích của việc này là vì vậy khi tôi stat mỗi tệp, tôi thêm kích thước của nó vào nút hiện tại và mỗi nút cha trong cây.

này là đủ để tạo ra:

username,300M 
username/documents,150M 
username/documents/excel,50M 
username/documents/word,40M 
username/work,70M 
username/fish,50M, 
username/some_other_stuff,30M 

Nhưng tôi muốn đến nay biến rằng trong để JSON hơn như thế này:

{ 
    "name" : "username", 
    "size" : "307200", 
    "children" : [ 
     { 
      "name" : "documents", 
      "size" : "153750", 
      "children" : [ 
        { 
         "name" : "excel", 
         "size" : "51200" 
        }, 
        { 
         "name" : "word", 
         "size" : "81920" 
        } 
      ] 
     } 
    ] 
} 

Đó là bởi vì tôi đang có ý định làm một D3 hình dung của cấu trúc này - dựa trên D3 Zoomable Circle Pack

Vì vậy, câu hỏi của tôi là - cách gọn gàng nhất để đối chiếu dữ liệu của tôi sao cho tôi có thể tích lũy (và lý tưởng nhất là không mulative) kích thước thông tin, nhưng populating một hash hierarchically.

Tôi đã suy nghĩ về một cách tiếp cận 'con trỏ' (và sử dụng File::Spec thời gian này):

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

Mặc dù ... đó là không hoàn toàn tạo ra các cấu trúc dữ liệu tôi đang tìm kiếm, không kém phần quan vì về cơ bản chúng tôi phải tìm kiếm bằng khóa băm để thực hiện phần 'cuộn lên' của quy trình.

Có cách nào tốt hơn để hoàn thành việc này không?

Edit - ví dụ hoàn chỉnh hơn về những gì tôi có đã:

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

Và vâng - Tôi biết điều này là không di động, và thực hiện một vài điều hơi khó chịu. Một phần của những gì tôi đang làm ở đây đang cố gắng cải thiện điều đó. (Nhưng hiện nay nó là một cấu trúc homedir dựa trên Unix, vì vậy đó là tốt).

+0

Bạn có thể thêm một ví dụ hoàn chỉnh mà tôi lười biếng có thể sao chép/dán xin vui lòng? – simbabque

+0

OK, chịu với tôi. Sẽ phải cắt tập lệnh của tôi xuống một chút. – Sobrique

+0

OK. Đã thêm một ví dụ tối thiểu. (Nó bỏ qua rất nhiều rác, giống như một số định dạng đơn vị và kết quả đầu ra tóm tắt khác nhau). – Sobrique

Trả lời

3

Nếu bạn làm quét dir của riêng bạn thay vì sử dụng File :: Tìm, bạn tự nhiên có cấu trúc đúng.

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

Phần còn lại của các mã:

#!/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] // '.'))); 
+0

Có lỗi chính tả trong '_' trong' -s _; 'và' -d _; '. – simbabque

+8

@ simbabque, tôi nghĩ bạn có nghĩa là tôi nên sử dụng '$ _' thay vì' _', nhưng bạn sẽ sai. '_' là một xử lý được điền bởi' stat' và 'lstat'. Bằng cách này, tôi chỉ thực hiện một cuộc gọi hệ thống và tôi chỉ cần kiểm tra lỗi một lần. – ikegami

+1

Đã thay đổi để ngừng theo dõi các liên kết tượng trưng – ikegami

0

Cuối cùng, tôi đã thực hiện nó như thế này:

Trong File::Find muốn phụ 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} //= {}; 
} 

Để tạo một hash của tên thư mục lồng nhau. (Phụ đề name có thể là thừa, nhưng bất kỳ điều gì).

Và sau đó bài xử lý nó với (sử dụng JSON):

my $json_structure = { 
    'name'  => $user, 
    'size'  => $data->{$user}->{size}, 
    'children' => [], 
}; 
process_data_to_json($json_structure, $data->{$user}->{children}); 
open(my $json_out, '>', "homedir.json") or die $!; 
print {$json_out} to_json($json_structure, { pretty => 1 }); 
close($json_out); 


sub process_data_to_json { 
    my ($json_cursor, $data_cursor) = @_; 
    if (ref $data_cursor eq "HASH") { 
     print "Traversing $key\n"; 
     my $newelt = { 
      'name' => $key, 
      'size' => $data_cursor->{$key}->{size}, 
     }; 
     push(@{ $json_cursor->{children} }, $newelt); 
     process_data_to_json($newelt, $data_cursor->{$key}->{children}); 
    } 
} 
+0

Tôi sẽ đề nghị một cái gì đó tương tự, nhưng không có con trỏ, làm việc xử lý của bạn theo cách bạn đã có nó trước và lặp qua cấu trúc cuối cùng để tạo ra một thứ hai để xử lý JSON. Nhưng chưa có thời gian để viết nó lên. – simbabque

Các vấn đề liên quan