From 72ae0c337e5748f255bc2073985aa189c7ab6b83 Mon Sep 17 00:00:00 2001 From: Mitch Tishmack Date: Sun, 9 Aug 2015 13:41:51 -0500 Subject: [PATCH] Add my old diskhog script to dotfiles. --- common/bin/diskhog | 151 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 common/bin/diskhog diff --git a/common/bin/diskhog b/common/bin/diskhog new file mode 100755 index 0000000..0c8c6de --- /dev/null +++ b/common/bin/diskhog @@ -0,0 +1,151 @@ +#!/usr/bin/env perl +# +# Find out who is hogging disk space and where. +# +# Argument is the directory to start from. Only calculates size of files. +# Adds the size of files in a directory to the size of the parent directory only. +# +# Defaults to top 10 directories/files. +# +# Override with -n num to list whatever amount of large things you desire. +# Or if you want to abuse the parser, use -1 for all files/directories. +# +# Defaults to pwd for space. +# + +use File::Find; +use Getopt::Long; +use Cwd; + +my $debug; +my $statbug; +(exists($ENV{"DEBUG"})) ? $debug++ : undef; +my $dir = Cwd::abs_path($ARGV[-1] || Cwd::getcwd()); +my %diskhogs = (); +my %inodes = (); +my ($topdev,undef,undef,undef,undef,undef,undef,undef) = lstat($dir); +local $opt_displaynum = 10; + +GetOptions( 'n=i', => \$opt_displaynum, + ); + +find(\&sieve, $dir); + +# +# For outputing the size of a bunch of bytes in g/m/k depending upon the size +# +sub byte_print { + my $byte_k = 1_024; + my $byte_m = $byte_k**2; + my $byte_g = $byte_k**3; + my $bytes = pop; + my $base = $bytes; + my $suffix = "nil"; + my $out_string = "BUG"; + if($bytes > $byte_g){ + $base = $bytes / $byte_g; + $suffix = "g"; + }elsif($bytes > $byte_m){ + $base = $bytes / $byte_m; + $suffix = "m"; + }else{ + $base = $bytes / $byte_k; + $suffix = "k"; + }; + $out_string = sprintf("%.2f%s", $base, $suffix); + return $out_string; +}; + +# +# Dumb function to return if an inode has already been seen. +# So we handle hard links sane(er-ish-ly) in size calculations. +# This has more truthiness(tm). +# +sub seeninode{ + my $what = pop(); + my $ret = 0; + unless(exists($inodes{$what})){ + $inodes{$what} = 0; + print "New inode $what added to seen inodes.\n" if $debug; + }else{ + $inodes{$what}++; + $ret++; + print "Found a hardlink for $what.\n" if $debug; + }; + return $ret; +}; + +sub sieve { + my $file = $File::Find::name; + my $filedir = $File::Find::dir; + my @statinfo = lstat($file); + my $dev = $statinfo[0]; + my $inode = $statinfo[1]; + my $size = $statinfo[7]; + my $blocksize = 512; # field 11 is the "preferred" block size, which appears + # to not match the actual blocksize of the filesystems + # in use. For now every hard drive uses 512bytes/block. + # Will revisit this when 4096byte/block hard drives arrive + my $apparentsize = $statinfo[12] * $blocksize; + + print "$file\@$filedir \<$topdev\>\=\<$dev\>\n" if $debug; + print "size,apparentsize,blocksize = <$statinfo[7],$apparentsize\,$blocksize\>\n" if $debug; + # + # Try to handle sparse files. + # + if ($size > $apparentsize) { + print "Sparse file $file!\n" if $debug; + $size = $apparentsize; + }; + + $dev = (-l $file) ? 0xdeadbeef : $dev; + if (($file =~ m/\/proc|\/chroot|udev\/devices/) || + ($dev != $topdev) || seeninode($inode)){ + print "Pruned $file.\n" if $debug; + $File::Find::prune = 1; + next; + }; + unless ($dev){ + # Yay, we can't l(stat) files. This version of perl is broken. + # Snag information from ls, assume it's a file. Thanks Solaris 8, you suck. + $statbug++; + warn "Hit empty device statbug while stat()ing file <$file>.\n"; + my $ls = qx(ls -ld $file); + my @raw = split(/\s+/, $ls); + $diskhogs{"$file"} = $raw[4]; + $diskhogs{"$filedir"} += $raw[4]; + }elsif (-f){ + if ($debug){ + print "Adding $file size ", &byte_print($size), " to $filedir.\n"; + print "Adding $file size ", &byte_print($size), "\n"; + }; + $diskhogs{"$file"} = $size; + $diskhogs{"$filedir"} += $size; + }elsif (-d){ + print "Adding $file size ".&byte_print($size)."\n" if $debug; + $diskhogs{"$file"} += $size; + }; + if ($debug) { + print "$file from $filedir has size ".&byte_print($diskhogs{"$file"})."\n"; + }; +}; + +# +# Reverse sort the keys by size. +# +my @sorted = sort {$diskhogs{$b} <=> $diskhogs{$a}} keys %diskhogs; + +splice @sorted, $opt_displaynum if @sorted > $opt_displaynum; + +warn "Found one or more instances of the stat() call statbug. This is normally only present on Solaris 8 with stock perl.\n" if $statbug; + +foreach (@sorted){ + printf "%-9s%s\n", &byte_print($diskhogs{$_}), $_; +}; + +if ($debug){ + print "DEBUG INFORMATION:\n"; + foreach (keys %diskhogs){ + printf "%-9s%s\n", &byte_print($diskhogs{$_}), $_; + }; +};