#!/usr/bin/perl -w # # find-du [--min=min-size] [--seperate-dir] [--one-file-system] [dir [dir]...] # # Find the leaf directories or directories plus their children consuming # more than min-size KiB, printing their sizes and names in depth-first # order. # # Default min-size is 50 MiB; otherwise set via --min=value. # Includes sub-dirs in dirsize; otherwise use --seperate-dir to only # include subdirs if smaller than min-size. # Traverse all filesystems; otherwise use --one-file-system. # # Default dir is cwd # $VERSION = "0.2"; # David N. Lombard, Thu Nov 11 23:27:50 PST 2004 # $VERSION = "0.3"; # David N. Lombard, Fri Nov 12 21:07:45 PST 2004 # Count hard links once and only once (in first dir found). # Don't forget to count size of symlinks. # Use lstat and S_IS* macros. # Make options more du(1)-like. # # Copyright, 2004, David N. Lombard # # find-du is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # find-du is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ($ToolName = $0) =~ s:.*/::; $ENV{PATH} = "/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin"; $|++; use Fcntl ':mode'; use Getopt::Long; Getopt::Long::Configure( "bundling", "require_order" ); my $pwd; chomp( $pwd = `pwd` ); my $help = 0; my $min = 50000; my $S = 0; my $x = 0; my $version = 0; my $ok = GetOptions( "help" => \$help, "min|m=s" => \$min, "seperate-dirs|S" => \$S, "one-file-system|x" => \$x, "version" => \$version, ); if( $version ) { print "$ToolName Version $VERSION\n"; exit 0; } $ok = 0 if $help; my $dir = shift @ARGV || $pwd; die "Usage: $ToolName [options] [dir [dir]...]\n" ."\t[--help]\n" ."\t[--min=min-size]\n" ."\t[--one-file-system | -x]\n" ."\t[--seperate-dirs | -S]\n" unless $min > 0 && $ok; my @items; my %hl; # # Recursive descent to find directory sizes. # sub rd { my ($min, $dev, $dir) = @_; my $total = 0; my $Stotal = 0; # if( opendir( DIR, $dir ) ) { foreach (readdir DIR) { next if $_ eq "." || $_ eq ".."; my $path = "$dir/$_"; my @st = lstat( $path ); next if defined $dev && $st[0] == $dev; if( S_ISDIR( $st[2] ) && !S_ISLNK( $st[2] ) ) { my $dtotal = rd( $min, $dev, $path ); if( $dtotal < $min ) { $total += $dtotal; } else { $Stotal += $dtotal; } } elsif( ($st[3]||1) == 1 || !$hl{"$st[0].$st[1]"}++ ) { $total += $st[12]/2 } } closedir DIR; $total += (stat $dir)[12]/2; $total += $Stotal unless $S; push @items, [$total, $dir] unless $total < $min; } $total += $Stotal if $S; return $total; } # # Descend each directory tree, calling out dirs with at least # $min KiB. # foreach ($dir, @ARGV) { s/\/$//; my $dev; $dev = (stat $_)[0] if $x; rd( $min, $dev, $_ ); } # # Print the results. # print "$_->[0]\t$_->[1]\n" foreach @items; exit 0;