DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Dispus.pl

12.18.2008
| 1698 views |
  • submit to reddit
        
#!/bin/sh
exec `which perl` -x. $0 $*
#!/usr/bin/perl -w
#
# @(#)dispus	2.4 09/29/03
#
# dispus - Browse a directory tree to find out where all the space went.
#
# Written by Annihilannic (annihilannic@hotmail.com)
#
#  Todo:
#
#	o Nothing at present.
#
#  Bugs:
#
#	o If a filesystem is automounted while running it will not be 
#	  recognised as a separate filesystem, but mounts aren't checked
#	  regularly for efficacity.
#	o Does not check secondary automounter configuration files, such
#	  as those used by a /- entry in the master file.
#
#  Version history:
#
# 2.4	o Changed the prompt to match the new name (Oops).
#	o Some changes for AIX compatibility.  Thanks to Lloyd Blauen
#	  for testing.
#
# 2.3	o Moved 'commas' code into a subroutine and used it to display
#	  'df' totals as well.
#	o Added 'x' option to quit at Ed Schaefer's request.
#	o Slightly changed 'df' output processing to recognise devices
#	  that don't begin with '/'.
#
# 2.2	o Renamed from 'dusage.pl' to 'dispus', a contraction of DIsk
#	  SPace USage (and apparently the Romanian work for "ready" or
#	  "willing") to avoid confusion with a similarly named script by 
#	  a fellow called John Vromans as well as a similar utility for 
#	  NetWare.
#	o Uses #!/bin/sh shebang at beginning of script to accommodate
#	  varying locations of 'perl'.
#	o Fixed yet another bug with space handling, this time while 
#	  viewing the contents of a directory whose name contains spaces.
#
# 2.1	o Starting using 'sccs' to manage code.
#	o Fixed incorrect counting of mount points.  An empty array has
#	  -1 items, not 0.
#
# 1.9	o Fixed handling of filenames with spaces by always using branch
#	  that handles ignoring mount points.
#
# 1.8	o Slight change to 'du' options selection based on OS (for Cygwin).
#	o Changed to display correct values from 'df'.
#
# 1.7	o Hidden files not listed.  Found a way to fix using an `ls` but
#	  runs into the filenames with spaces bug again.
#	o Include free space on filesystem after current directory name.
#
# 1.6	o Fixed bug with determining mount points under Linux (Solaris
#	  says "(filesystem) on (device)" unless you use 'mount -v',
#	  in which case the sense is reversed, like Linux).
#	o Accommodated different auto master filename on Linux.
#
# 1.5	o Reads /etc/auto_master to establish further mount points to
#	  ignore.
#
# 1.4	o Displays 'Ignoring mount points:' if the current directory
#	  is a mount point, though it may not contain any. - bug fixed.
#
# 1.3	o Code improvements (use /string/i in regexp matches).
#	o Ignore mount points in any directory.
#
# 1.2	o Accept 'c' or 'cd' and a parameter.
#	o Remembered to increment version number.
#       o "!shell command"
#
# 1.1 	o Accept 'quit', 'exit'.
#	o Changed '.' command to reread usage in current directory in
#	  case files have been deleted.
#
# 1.0 	o Linux compatibility (different du switches).
#
# 0.9 	o Ignore mount points in root directory.
#
# 0.8 	o Used KB (Bytes) instead of Kb (Kilobits).
#	o Added -d switch to du to restrict it to that particular 
#	  filesystem.
#	o Added 'k' command to start Korn shell instead of Bourne as
#	  root's SHELL variable always seems to be '/bin/sh' regardless.
#
# 0.7 	o Directories containing a space in their name are not handled
#	  correctly by the split. - fixed.
#	o Fixed prompt on invalid selection.
#	o Terminated 'du' command with -- to handle files beginning with
#	  '-' character.
#
# 0.6	o Included help, new prompt, shell function.
#	o Started version history.

# Initialise a few things.

sub init {

	$version="dispus v2.4";

	$S_IFDIR=0x4000;
	$PROMPT="\ndispus> ";

	# Number of lines used on screen apart from actual file listing.
	$linesreserved=6; 

	# Set $pagelength by using:
	#  - the LINES environment variable,
	#  - 'stty rows' value or 
	#  - the default value, in that order.
	$pagelength=$ENV{"LINES"};
	if (!defined($pagelength)) { 
		open(STTY,"stty |") || die "Error running stty: $!";
		while (<STTY>) {
			/^rows = ([0-9]+)/; 
			if (defined($1)) { $pagelength=$1 };
		}
		close(STTY);
	}
	if (!defined($pagelength)) { $pagelength=24 };

	# Set OS dependent variables.
	chomp($os=`uname`);
	if ($os eq "Linux") {
		$du="du -xks -- ";
		$automaster="/etc/auto.master";
		$mount="mount -v | "
	} elsif ($os eq "SunOS"){
		$du="du -dks -- ";
		$automaster="/etc/auto_master";
		$mount="mount -v | "
	} elsif ($os eq "AIX"){
		$du="du -ks -- ";
		# VERIFY
		$automaster="/etc/auto_master";
		$mount="mount | ";
	} else {
		$du="du -ks -- ";
		$automaster="/etc/auto_master";
		$mount="mount -v | "
	}

	open(MOUNTPOINTS, $mount) or die "Error running mount: $!";
	$i=0;
	if ($os eq "AIX") {
		<MOUNTPOINTS>; # Skip first two lines (column headings)
		<MOUNTPOINTS>;
		while (<MOUNTPOINTS>) {
			# Account for lines with empty node (first) column.
			if (/^ +[^ ]+ +([^ ]+) /) { $mountpoints[$i++]=$1; }
			# Account for lines with non-empty node column, 
			#e.g. NFS mounts.
			if (/^[^ ]+ +[^ ]+ +([^ ]+) /) { $mountpoints[$i++]=$1; }
		}
	} else {
		while (<MOUNTPOINTS>) {
			if (/^.*\son\s(\/.*?)?\s.+\n/) { $mountpoints[$i++]=$1; }
		}
	}
	close(MOUNTPOINTS);

	if (open(MOUNTPOINTS, $automaster)) {
		while (<MOUNTPOINTS>) {
			if (/^(\/.*?)\s.+$/) { $mountpoints[$i++]=$1; }
		}
		close(MOUNTPOINTS);
	}
}

# Put commas in number(s) to make them easy to read.

sub commas {
	return unless defined wantarray;  # void context, do nothing
	my @parms = @_;
	for (@parms) { 
		if ($_ =~ s/(\d+)(\d\d\d)$/$1,$2/ == 1) {
			until ( ($_ =~ s/(\d+)(\d\d\d),/$1,$2,/) != 1) { };
		}
	}
	return wantarray ? @parms : $parms[0];
}

# Run du and populate @usage and @dirname.

sub readusage {
	chomp($cwd=`pwd`);
	print("$version - Reading usage in ",$cwd,"\n");

	# Determine whether there are any mountpoints in the current
	# directory.
	$i=0;
	@cwdmountpoints=();
	foreach (@mountpoints) {
		if ($cwd eq "/" && m@^/([^/]+)$@) {
			$cwdmountpoints[$i++] = $1;
		} elsif (m@^$cwd/([^/]+)$@) { 
			$cwdmountpoints[$i++] = $1;
		}
	}

	# Ignore any mountpoints in the current directory.
	open(LS, "ls -d \"" . $cwd . "\"/* \"" . $cwd . "\"/.[!.]* 2> /dev/null |") 
		or die "Error running ls: $!";
	chomp(@files=(<LS>));
	close(LS);

	# Strip off pathnames to clarify output, and quote filenames with
	# spaces.
	foreach (@files) {
		s/^.*\///;
		s/^(.*[() ].*)$/\"$1\"/;
	}

	if ($#cwdmountpoints >= 0) {
		print "Ignoring mount points:";
		foreach (@cwdmountpoints) {
			for ($i=0; $i <= $#files; $i++) {
				# Remove any mount points.
				if ($files[$i] eq $_) {
					splice(@files,$i,1);
					print " $_"
				}
			}
		}
		print "\n";
	}

	$ducmd = $du . join(" ",@files) . " | sort -n -r |";

	open(DUKS, $ducmd) or die "Error running du: $!";
	@duks=(<DUKS>);
	close(DUKS);
	chomp(@duks);

	$i=1;
	$#usage=0;
	foreach (@duks) {
		/([0-9]+)\s+(.*)/;
		$usage[$i] = $1;
		$dirname[$i] = $2;
		$i++;
	}
	$topofpage=1;
}

# Display the contents of @usage and @dirname neatly.

sub displaydusage {
	$i=$topofpage;
	printf("\n");
	if ($topofpage==1) {
		$templinesreserved=$linesreserved + 2;

		# Display space used on this filesystem.
		open(DF, "df -k $cwd 2> /dev/null | ") or die "Error running df: $!";
		while (<DF>) {
			if (/^.* +([0-9]+) +([0-9]+) +[0-9]+ +([0-9]+%)/) {
				print("   " . commas($2) . " KB used of " . commas($1) . " KB available ($3)\n\n"); 
			}
		}
		close(DF);

	} else {
		$templinesreserved=$linesreserved;
	}
	while ($i <= $#usage 
		&& $i-$topofpage < $pagelength-$templinesreserved) {

		# Insert commas in the number.
		#if ($usage[$i] =~ s/(\d+)(\d\d\d)$/$1,$2/ == 1) {
			#until (	($usage[$i] =~ s/(\d+)(\d\d\d),/$1,$2,/) != 1) {};
		#}
		$mode = (stat($dirname[$i]))[2];
		if ( defined($mode) && ($S_IFDIR & $mode)) {
			printf("%4d. %10s KB   %s\n",$i,commas($usage[$i]),$dirname[$i]);
		} else {
			printf("      %10s KB   %s\n",commas($usage[$i]),$dirname[$i]);
		}
		$i++;
	}
	$i--;   # Undo the overrun.
	print "\n $topofpage to $i of $#usage shown   ? - help";
	if ($i != $#usage) { print "   f - forward" };
	if ($topofpage != 1) { print "   b - back" };
	print "   q - quit\n";
}

# Main code.

init;
if ($#ARGV >= 0) {
	chdir($ARGV[0]) || die "Unable to change directory to $ARGV[0]: $!";
}
readusage;
displaydusage;
print $PROMPT;
WHILE: while (<STDIN>) {
	chomp;
	CASE: {
		/^\?$/ && do { 
			print "\n";
			print "?	Display this help\n";
			print "0-9+	Traverse selected directory\n";
			print ".	Redisplay current directory\n";
			print "..	Traverse parent directory\n";
			print "c [dir]	Change to specific directory\n";
			print "f	Page forward\n";
			print "b	Page back\n";
			print "q	Quit\n";
			print "s	Start \$SHELL in current directory\n";
			print "k	Start /bin/ksh in current directory\n";
			print "![cmd]	Run command 'cmd' (or shell if omitted) in current directory\n";
			last CASE;
		};
		/^[0-9]+$/ && do { 
			print "\n";
			chdir($dirname[$_]);
			readusage;
			displaydusage;
			last CASE;
		};
		/^\.$/ && do { 
			readusage;
			displaydusage;
			last CASE;
		};
		/^\.\.$/ && do { 
			chdir("..");
			readusage;
			displaydusage;
			last CASE;
		};
		/^cd?( (.+))?$/i && do { 
			if (defined($2)) {
				$newdir=$2;
			} else {
				print "Enter directory to change to: ";
				chomp($newdir=<STDIN>);
			}
			if (chdir($newdir)) {
				readusage; 
				displaydusage;
			} else {
				print "chdir to $newdir failed!\n";
			}
			last CASE;
		};
		/^b$/i && do { 
			$topofpage=$topofpage-($pagelength-$linesreserved);
			if ($topofpage < 1) { $topofpage=1 }
			displaydusage;
			last CASE; 
		};
		/^f$/i && do { 
			$topofpage=$i;
			displaydusage;
			last CASE; 
		};
		/^k$/i && do { 
			system "/bin/ksh";
			displaydusage;
			last CASE; 
		};
		/^[s!](.+)?$/i && do { 
			if (defined($1)) {
				system($1);
			} else {
				system "\$SHELL";
				displaydusage;
			}
			last CASE;
		};
		/^(q.*|exit|x)$/i && do { last WHILE; };
		print "Invalid selection!  ? for help.\n";
	}
	print $PROMPT;
}
print "\n";