2015-09-03 5 views
6

मैं एक डिस्क स्पेस रिपोर्ट कर रहा हूं जो निर्देशिका पेड़ में संचयी आकार एकत्र करने के लिए File::Find का उपयोग करता है।JSON डेटा संरचना में फ़ाइल पथ

File::Find से मुझे क्या मिलता है (आसानी से) निर्देशिका का नाम है।

उदा .:

/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" 
        } 
      ] 
     } 
    ] 
} 

क्योंकि मैं एक डी 3 करने के लिए इच्छुक हूँ कि इस संरचना का विज़ुअलाइजेशन - D3 Zoomable Circle Pack

तो मेरा प्रश्न यह है - मेरे डेटा को एकत्र करने का सबसे अच्छा तरीका क्या है कि मैं संचयी (और आदर्श गैर cu संचयी) आकार की जानकारी, लेकिन एक हैश पदानुक्रमित रूप से populating।

मैं एक 'कर्सर' दृष्टिकोण के मामले में सोच रहा था (और 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"; 
    } 
} 

और हाँ - मुझे पता है यह पोर्टेबल नहीं है, और कुछ हद तक एक बुरा काम करता है। मैं जो कर रहा हूं उसका हिस्सा उस पर सुधार करने की कोशिश कर रहा है। (लेकिन वर्तमान में यह यूनिक्स आधारित होमडियर संरचना है, तो यह ठीक है)।

+0

आप एक पूरा उदाहरण है कि मैं lazily कॉपी कर सकते हैं/पेस्ट कृपया जोड़ सकते हैं? – simbabque

+0

ठीक है, मेरे साथ भालू। मुझे अपनी स्क्रिप्ट को थोड़ा आकार देने के लिए ट्रिम करना होगा। – Sobrique

+0

ठीक है। एक न्यूनतम उदाहरण जोड़ा गया। (यह बहुत सारे जंक को छोड़ देता है, जैसे यूनिट स्वरूपण और विभिन्न सारांश आउटपुट)। – Sobrique

उत्तर

3

:: पता लगाएं, आप स्वाभाविक सही संरचना मिलता है।

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

'_' में' _' _ '' और '-d _;' में टाइपो हैं। – simbabque

+8

@ सिंबैब, मुझे लगता है कि मेरा मतलब है कि मुझे '_' के बजाय' $ _' का उपयोग करना चाहिए था, लेकिन आप गलत होंगे। '_' एक 'हैंडल' और 'lstat' द्वारा आबादी वाला एक हैंडल है। इस तरह, मैं केवल एक सिस्टम कॉल करता हूं, और मुझे केवल एक बार त्रुटि की जांच करने की आवश्यकता है। – ikegami

+1

सिम्लिंक के बाद रोकने के लिए बदला गया – ikegami

0

अंत में, मैं इसे इस तरह किया है:

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 का प्रयोग करके) के साथ: आप के बजाय फ़ाइल का उपयोग करने के अपने खुद के dir स्कैनिंग करते हैं

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

मैं कुछ इसी तरह का सुझाव देने जा रहा था, लेकिन कर्सर के बिना, जिस तरह से आप पहले से ही पहले थे और अपनी संरचना को कर रहे थे और जेएसओएन प्रोसेसिंग के लिए दूसरा निर्माण करने के लिए अंतिम संरचना पर पुनरावृत्ति कर रहे थे। लेकिन अभी तक इसे लिखने का समय नहीं था। – simbabque

संबंधित मुद्दे