File Coverage

blib/lib/Filesys/DiskUsage.pm
Criterion Covered Total %
statement 59 62 95.1
branch 32 40 80.0
condition 14 18 77.7
subroutine 5 5 100.0
pod 1 1 100.0
total 111 126 88.1


line stmt bran cond sub pod time code
1             package Filesys::DiskUsage;
2              
3 3     3   92394 use warnings;
  3         6  
  3         113  
4 3     3   12 use strict;
  3         4  
  3         72  
5              
6 3     3   12 use File::Basename;
  3         7  
  3         2337  
7              
8             =head1 NAME
9              
10             Filesys::DiskUsage - Estimate file space usage (similar to `du`)
11              
12             =cut
13              
14             require Exporter;
15              
16             our @ISA = qw(Exporter);
17              
18             our %EXPORT_TAGS = (
19             'all' => [ qw(
20             du
21             ) ],
22             );
23              
24             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
25              
26             our @EXPORT = qw(
27             );
28              
29             our $VERSION = '0.08';
30              
31             =head1 SYNOPSIS
32              
33             use Filesys::DiskUsage qw/du/;
34              
35             # basic
36             $total = du(qw/file1 file2 directory1/);
37              
38             or
39              
40             # no recursion
41             $total = du( { recursive => 0 } , <*> );
42              
43             or
44              
45             # max-depth is 1
46             $total = du( { 'max-depth' => 1 } , <*> );
47              
48             or
49              
50             # get an array
51             @sizes = du( @files );
52              
53             or
54              
55             # get a hash
56             %sizes = du( { 'make-hash' => 1 }, @files_and_directories );
57              
58             =head1 FUNCTIONS
59              
60             =head2 du
61              
62             Estimate file space usage.
63              
64             Get the size of files:
65              
66             $total = du(qw/file1 file2/);
67              
68             Get the size of directories:
69              
70             $total = du(qw/file1 directory1/);
71              
72             =head3 OPTIONS
73              
74             =over 6
75              
76             =item dereference
77              
78             Follow symbolic links. Default is 0. Overrides C.
79              
80             Get the size of a directory, recursively, following symbolic links:
81              
82             $total = du( { dereference => 1 } , $dir );
83              
84             =item exclude => PATTERN
85              
86             Exclude files that match PATTERN.
87              
88             Get the size of every file except for dot files:
89              
90             $total = du( { exclude => qr/^\./ } , @files );
91              
92             =item human-readable
93              
94             Return sizes in human readable format (e.g., 1K 234M 2G)
95              
96             $total = du ( { 'human-readable' => 1 } , @files );
97              
98             =item Human-readable
99              
100             Return sizes in human readable format, but use powers of 1000 instead
101             of 1024.
102              
103             $total = du ( { 'Human-readable' => 1 } , @files );
104              
105             =item make-hash
106              
107             Return the results in a hash.
108              
109             %sizes = du( { 'make-hash' => 1 } , @files );
110              
111             =item max-depth
112              
113             Sets the max-depth for recursion. A negative number means there is no
114             max-depth. Default is -1.
115              
116             Get the size of every file in the directory and immediate
117             subdirectories:
118              
119             $total = du( { 'max-depth' => 1 } , <*> );
120              
121             =item recursive
122              
123             Sets whether directories are to be explored or not. Set to 0 if you
124             don't want recursion. Default is 1. Overrides C.
125              
126             Get the size of every file in the directory, but not directories:
127              
128             $total = du( { recursive => 0 } , <*> );
129              
130             =item sector-size => NUMBER
131              
132             All file sizes are rounded up to a multiple of this number. Any file
133             that is not an exact multiple of this size will be treated as the next
134             multiple of this number as they would in a sector-based file system. Common
135             values will be 512 or 1024. Default is 1 (no sectors).
136              
137             $total = du( { sector-size => 1024 }, <*> );
138              
139             =item show-warnings => 1 | 0
140              
141             Shows warnings when trying to open a directory that isn't readable.
142              
143             $total = du( { 'show-warnings' => 0 }, <*> );
144              
145             1 by default.
146              
147             =item symlink-size => NUMBER
148              
149             Symlinks are assumed to be this size. Without this option, symlinks are
150             ignored unless dereferenced. Setting this option to 0 will result in the
151             files showing up in the hash, if C is set, with a size of 0.
152             Setting this option to any other number will treat the size of the symlink
153             as this number. This option is ignored if the C option is
154             set.
155              
156             $total = du( { symlink-size => 1024, sector-size => 1024 }, <*> );
157              
158             =item truncate-readable => NUMBER
159              
160             Human readable formats decimal places are truncated by the value of
161             this option. A negative number means the result won't be truncated at
162             all. Default if 2.
163              
164             Get the size of a file in human readable format with three decimal
165             places:
166              
167             $size = du( { 'human-readable' => 1 , 'truncate-readable' => 3 } , $file);
168              
169             =back
170              
171             =cut
172              
173             my %all;
174             sub du {
175             # options
176 43     43 1 4016 my %config = (
177             'dereference' => 0,
178             'exclude' => undef,
179             'human-readable' => 0,
180             'Human-readable' => 0,
181             'make-hash' => 0,
182             'max-depth' => -1,
183             'recursive' => 1,
184             'sector-size' => 1,
185             'show-warnings' => 1,
186             'symlink-size' => undef,
187             'truncate-readable' => 2,
188             );
189 43 100       118 if (ref($_[0]) eq 'HASH') {%config = (%config, %{+shift})}
  38         85  
  38         518  
190 43   100     221 $config{human} = $config{'human-readable'} || $config{'Human-readable'};
191              
192 43         135 my $calling_sub = (caller(1))[3];
193 43 100 100     293 if (not defined $calling_sub or $calling_sub ne 'Filesys::DiskUsage::du') {
194 20         42 %all = ();
195             }
196 43         34 my %sizes;
197              
198             # calculate sizes
199 43         65 for (@_) {
200 124 100       190 next if exists $all{$_};
201 123         156 $all{$_} = 0;
202 123 50 66     533 if (defined $config{exclude} and -f || -d) {
      66        
203 39         724 my $filename = basename($_);
204 39 100       160 next if $filename =~ /$config{exclude}/;
205             }
206 110 100       1519 if (-l) { # is symbolic link
    100          
    50          
207 2 100       5 if ($config{'dereference'}) { # we want to follow it
208 1         11 $sizes{$_} = du( { 'recursive' => $config{'recursive'},
209             'exclude' => $config{'exclude'},
210             'sector-size' => $config{'sector-size'},
211             'dereference' => $config{'dereference'},
212             }, readlink($_));
213             }
214             else {
215 1 50       6 $sizes{$_} = $config{'symlink-size'} if defined $config{'symlink-size'};
216 1         2 next;
217             }
218             }
219             elsif (-f) { # is a file
220 77         460 $sizes{$_} = $config{'sector-size'} - 1 + -s;
221 77         147 $sizes{$_} -= $sizes{$_} % $config{'sector-size'};
222             }
223             elsif (-d) { # is a directory
224 31 100 100     118 if ($config{recursive} && $config{'max-depth'}) {
225              
226 22 50       370 if (opendir(DIR, $_)) {
    0          
227 22         30 my $dir = $_;
228 22         200 my @files = readdir DIR;
229 22         121 closedir(DIR);
230              
231 72         143 $sizes{$_} += du( { 'recursive' => $config{'recursive'},
232             'max-depth' => $config{'max-depth'} -1,
233             'exclude' => $config{'exclude'},
234             'sector-size' => $config{'sector-size'},
235             'show-warnings' => $config{'show-warnings'},
236             'dereference' => $config{'dereference'},
237 22         114 }, map {"$dir/$_"} grep {! /^\.\.?$/} @files);
  116         218  
238             }
239             elsif ( $config{'show-warnings'} ) {
240             # if the user requests to be notified of non openable directories, notify the user
241 0         0 warn "could not open $_ ($!)\n";
242             }
243              
244             }
245             }
246             }
247              
248             # return sizes
249 43 100       70 if ( $config{'make-hash'} ) {
250 1         3 for (keys %sizes) {$sizes{$_} = _convert($sizes{$_}, %config)}
  1         3  
251              
252 1 50       8 return wantarray ? %sizes : \%sizes;
253             }
254             else {
255 42 100       52 if (wantarray) {
256 1         4 return map {_convert($_, %config)} @sizes{@_};
  8         13  
257             }
258             else {
259 41         37 my $total = 0;
260 41         62 for (values %sizes) {$total += $_}
  91         80  
261              
262 41         108 return _convert($total, %config);
263             }
264             }
265              
266             }
267              
268             # convert size to human readable format
269             sub _convert {
270 50 50   50   86 defined (my $size = shift) || return undef;
271 50         154 my $config = {@_};
272 50 100       339 $config->{human} || return $size;
273 4 100       8 my $block = $config->{'Human-readable'} ? 1000 : 1024;
274 4         8 my @args = qw/B K M G/;
275              
276 4   33     17 while (@args && $size > $block) {
277 0         0 shift @args;
278 0         0 $size /= $block;
279             }
280              
281 4 100       9 if ($config->{'truncate-readable'} > 0) {
282 2         24 $size = sprintf("%.$config->{'truncate-readable'}f",$size);
283             }
284              
285 4         29 "$size$args[0]";
286             }
287              
288             =head1 AUTHOR
289              
290             Jose Castro, C<< >>
291              
292             =head1 COPYRIGHT & LICENSE
293              
294             Copyright 2004 Jose Castro, All Rights Reserved.
295              
296             This program is free software; you can redistribute it and/or modify it
297             under the same terms as Perl itself.
298              
299             =cut
300              
301             1; # End of Filesys::DiskUsage