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   107293 use warnings;
  4         6  
  4         110  
4 4     4   15 use strict;
  4         4  
  4         66  
5              
6 4     4   12 use File::Basename;
  4         7  
  4         250  
7              
8 4     4   13 use constant BYTES_PER_BLOCK => 512;
  4         3  
  4         2658  
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.09';
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 7331 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       113 if (ref($_[0]) eq 'HASH') {%config = (%config, %{+shift})}
  40         86  
  40         248  
204 46   66     172 $config{human} = $config{'human-readable'} || $config{'Human-readable'};
205              
206 46         159 my $calling_sub = (caller(1))[3];
207 46 100 100     265 if (not defined $calling_sub or $calling_sub ne 'Filesys::DiskUsage::du') {
208 21         68 %all = ();
209             }
210 46         33 my %sizes;
211              
212             # calculate sizes
213 46         81 for (@_) {
214 126 100       232 next if exists $all{$_};
215 125         145 $all{$_} = 0;
216 125 50 66     519 if (defined $config{exclude} and -f || -d) {
      66        
217 39         652 my $filename = basename($_);
218 39 100       155 next if $filename =~ /$config{exclude}/;
219             }
220 112 100       1514 if (-l) { # is symbolic link
    100          
    50          
221 2 100       4 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         13 'dereference' => $config{'dereference'},
227             }, readlink($_));
228             }
229             else {
230 1 50       4 $sizes{$_} = $config{'symlink-size'} if defined $config{'symlink-size'};
231 1         1 next;
232             }
233             }
234             elsif (-f) { # is a file
235 77         425 my @stat = stat(_);
236 77 50       126 if (defined $stat[0]) {
237 77 50       86 if ($config{blocks}) {
238 0         0 $sizes{$_} = $stat[12] * BYTES_PER_BLOCK;
239             } else {
240 77         93 $sizes{$_} = $config{'sector-size'} - 1 + $stat[7];
241 77         136 $sizes{$_} -= $sizes{$_} % $config{'sector-size'};
242             }
243             }
244             }
245             elsif (-d) { # is a directory
246 33 100 66     86 if ($config{recursive} && $config{'max-depth'}) {
247              
248 24 50       384 if (opendir(my $dh, $_)) {
    0          
249 24         30 my $dir = $_;
250 24         193 my @files = readdir $dh;
251 24         144 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         120 }, map {"$dir/$_"} grep {! /^\.\.?$/} @files);
  73         138  
  121         226  
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       64 if ( $config{'make-hash'} ) {
273 1         3 for (keys %sizes) {$sizes{$_} = _convert($sizes{$_}, %config)}
  1         2  
274              
275 1 50       19 return wantarray ? %sizes : \%sizes;
276             }
277             else {
278 45 100       55 if (wantarray) {
279 1         4 return map {_convert($_, %config)} @sizes{@_};
  8         14  
280             }
281             else {
282 44         26 my $total = 0;
283 44         68 for (values %sizes) {$total += $_}
  93         71  
284              
285 44         108 return _convert($total, %config);
286             }
287             }
288              
289             }
290              
291             # convert size to human readable format
292             sub _convert {
293 53 50   53   88 defined (my $size = shift) || return undef;
294 53         155 my $config = {@_};
295 53 100       352 $config->{human} || return $size;
296 4 100       8 my $block = $config->{'Human-readable'} ? 1000 : 1024;
297 4         6 my @args = qw/B K M G/;
298              
299 4   33     19 while (@args && $size > $block) {
300 0         0 shift @args;
301 0         0 $size /= $block;
302             }
303              
304 4 100       7 if ($config->{'truncate-readable'} > 0) {
305 2         24 $size = sprintf("%.$config->{'truncate-readable'}f",$size);
306             }
307              
308 4         26 "$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