File Coverage

blib/lib/Filesys/DiskUsage.pm
Criterion Covered Total %
statement 65 69 94.2
branch 34 44 77.2
condition 12 18 66.6
subroutine 6 6 100.0
pod 1 1 100.0
total 118 138 85.5


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