#!/bin/sh -- # A comment mentioning perl, indented bash's sake. eval 'exec perl -S $0 ${1+"$@"}' if 0; $sleep = 2.0; # seconds between updates $width = 1; # floating point fraction width $xterm = 0; # launch an xterm or not. $rate_only = 0; # only show the rates, not counts. $clear_mode = 'internal'; # or 'external' (calls system()) $debug = 0; $File0 = "/proc/net/dev"; # Default file select(STDERR); $| = 1; select(STDOUT); $| = 1; chop($Program = `basename $0`); $Usage = <<"END"; $Program: Continuously display rate of change of numbers in a file. (e.g. counts in /proc/* stats files) Usage: $Program Options: -f Watch (Default: $File0) -s Sleep seconds between updates (Default: $sleep) -w Width of floating point field (Default: $width) -x Start the process up in an xterm of just about the right size. -ro Show the rate only, not the total counts. -both Show both the total stats and the rates. (Default) -net Watch /proc/net/dev -inter Watch /proc/interrupts -stat Watch /proc/stat Notes: If "file" is a command like "ls -l foo|", then the rates for the command output will be measured. In this case the rate of growth of the file can be seen. END @A0 = @ARGV; LOOP: while (@ARGV) { $_ = shift; CASE: { /^-f$/ && ($File = shift, last CASE); /^-s$/ && ($sleep = shift, last CASE); /^-x$/ && ($xterm = 1, last CASE); /^-n$/ && ($width = shift, last CASE); /^-d$/ && ($debug = 1, last CASE); /^-ro/ && ($rate_only = 1, last CASE); /^-both/ && ($rate_only = 0, last CASE); /^-net/ && ($File = "/proc/net/dev", last CASE); /^-int/ && ($File = "/proc/interrupts", last CASE); /^-stat/ && ($File = "/proc/stat", last CASE); /^--$/ && (last LOOP); # -- means end of switches /^-(-.*)$/ && (unshift(@ARGV, $1), last CASE); /^(-h|-help)$/ && ((print $Usage), exit 0, last CASE); if ( /^-(..+)$/ ) { # split bundled switches: local($y, $x) = ($1, ''); foreach $x (reverse(split(//, $y))) { unshift(@ARGV,"-$x") }; last CASE; } /^-/ && ((print "Invalid arg: $_\n$Usage"), exit 1, last CASE); unshift(@ARGV,$_); last LOOP; } } if ( ! $File && $ARGV[0] ) { $File = $ARGV[0]; } if ( ! $File ) { $File = $File0; # Default file } if ( $File !~ /\|/ && ! -f $File ) { print STDERR "File does not exist: $File\n"; exit 1; } if ( $xterm && ! $ENV{RATE_LAUNCHED_XTERM} ) { open(PROC, "$File") || die "$!"; $max_width = 0; $lines = 0; while () { $len = length($_); $max_width = $len if $len > $max_width; $lines++; } $max_width += 5; if ( $rate_only ) { $lines = $lines + 3; } else { $lines = 2 * $lines + 5; } $ENV{RATE_LAUNCHED_XTERM} = 1; for ($i=0; $i<= $#A0; $i++) { if ($A0[$i] =~ /^-f/ ) { $A0[$i+1] = '"' . $A0[$i+1] . '"'; } } $cmd = "xterm -title \"rate:$File\" -geometry ${max_width}x${lines} -e $0 "; $cmd2 = join(' ', @A0); $cmd2 =~ s/([*\$!<>|])/\\$1/g; $cmd .= $cmd2; print STDERR "cmd: $cmd\n"; system("$cmd &"); exit 0; } $Last = &get_stats(); &clear(); print STDOUT $File . ":\n"; print STDOUT $Last; while (1) { &short_sleep($sleep); $Now = &get_stats(); &update($Last, $Now, $sleep); $Last = $Now; } exit 0; sub update { local($old, $new, $dt) = @_; $old =~ s/:/: /g; $new =~ s/:/: /g; $old =~ s/^(\d)/__SHIFT__ $1/; $old =~ s/\n(\d)/\n__SHIFT__ $1/g; $tmpl = $old; # todo: # -> __PNDSIGN__ while ($tmpl =~ /\s(\d+)\s/ ) { $val = $1; $val =~ s/\d/#/g; $tmpl =~ s/(\s)(\d+)(\s)/$1$val$3/; } $count = 0; foreach $word (split(/\s+/, $old)) { if ( $word =~ /^\d+$/ ) { $Old[$count++] = $word; } } $count = 0; foreach $word (split(/\s+/, $new)) { if ( $word =~ /^\d+$/ ) { $New[$count++] = $word; } } for ($i=0; $i < $count; $i++) { $len = length($New[$i]); if ( $New[$i] eq $Old[$i] ) { $rate = '0'; } else { $rate = ($New[$i] - $Old[$i])/(1.0*$dt); } if ( $rate eq '0' || $len <= 3) { $fmt = "%${len}d"; $rate = int($rate); $rate = sprintf($fmt, $rate); } else { $fmt = "%$len.${width}f"; $rate = sprintf($fmt, $rate); } $tmpl =~ s/(#+)/$rate/; } $old =~ s/__SHIFT__ //g; $new =~ s/__SHIFT__ //g; $tmpl=~ s/__SHIFT__ //g; &clear(); if ( $rate_only ) { print STDOUT "$File: Counts/sec:\n" . $tmpl . "\n"; } else { print STDOUT $File . ":\n"; print STDOUT $new; print STDOUT "\nCounts/sec:\n" . $tmpl . "\n"; } } sub trim { local($x) = @_; $x =~ s/^\s*//; $x =~ s/\s*$//; return $x; } sub get_stats { local($x); local($mode) = 'read'; # or cat $x = ''; if ( $mode eq 'cat' ) { $x = `cat $File`; } elsif ( $mode = 'read' ) { open(PROC, "$File") || return ''; $x = ''; while () { $x .= $_ } close(PROC); } return $x; } sub short_sleep { local($time) = $_[0]; select(undef, undef, undef, $time); } sub clear { if ( $clear_mode eq 'external' ) { system("clear"); } elsif ( $clear_mode eq 'internal' ) { if ( $clear_string eq '' ) { $tmp = "/tmp/clear_string.$$"; system("clear > $tmp"); open(CLEAR, "$tmp") || ($clear_mode = 'external'); $n = read(CLEAR, $clear_string, 1024); if ( $n >= 1024 ) { $clear_mode = 'external'; } close(CLEAR); unlink($tmp); print STDERR "clear_string length: ", length(clear_string), "\n" if $debug; sleep 1; } print STDOUT $clear_string; } }