Hey There,
Today, we're going to take a look at a simple shell script to monitor disk space usage. It's been quite a while since we've touch on that, going back to a post from last November regarding finding space hogs on overlay mounts. The script has been kept simple (basically checking every partition for one fixed percentage full) to highlight other features.
The main intent here was to set up a monitor that would be able to handle a variety of Linux and Unix Operating Systems (all dependant on the "uname -s" output from that system) and focus on that area distinctly. We've limited our initial list to HP-UX, Solaris, SCO and OpenBSD.
In this case we're using a simple case statement to enumerate through the four *nix's we have listed here. Obviously, we could easily add more operating systems, and their variations of the "df" command, to our list and, if it ever got too big, either roll them into an array or simplify the script so that more OS's would fall under the same umbrella.
Also, notice that we're stepping through parsing of the df output more tediously than is actually necessary. For instance, the creation of the df output could be parsed with sed all in one fell swoop. Again, although it is generally considered best practice to compact your script/code, our hope here was that this would be easy to follow for as many people as possible. Some folks learn better by tackling the tough-stuff and working back to basics and some of us learn better by starting with the basics and putting them all together to create the tough-stuff. It's a long and convoluted statement of philosophy, to be sure, but fairly descriptive of what we actually mean ;)
If you prefer, on the line where we parse the TABLE file and trim it with sed, you can take out this part (or add to it) as it was placed in there as an example of how to ignore a specific partition (/usr):
-e '/usr$/d'
So, the line:
sed -e '1d' -e '/usr$/d' ${BASEDIR}/TABLE >> ${BASEDIR}/TABLE2
could be changed to:
sed -e '1d' ${BASEDIR}/TABLE >> ${BASEDIR}/TABLE2
which could then be simplified even further (since you don't need to use -e, even though it's okay to, if you only have one instruction to pass to sed) to become this:
sed '1d' ${BASEDIR}/TABLE >> ${BASEDIR}/TABLE2
And the entire script could be thusly compacted and streamlined, etc.
In any event, I hope this can be of some help (or, at least, an inspiration to reach higher ;) to you!
Best wishes,
This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License#!/bin/ksh
#
# dfvk.sh - Check partition % full
# across multiple OS'
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
trap 'rm -f ${BASEDIR}/TABLE ${BASEDIR}/TABLE2 ${BASEDIR}/MAILER;exit 1' 1 2 3 15
#BASEDIR="/tmp"
BASEDIR="tmp"
LOGHOST=`hostname`
MAILEES="you@yourhost.com"
LIMIT=90
OSVERSION=`uname -s`
case $OSVERSION in
SunOS | OpenBSD )
df -k >> ${BASEDIR}/TABLE
;;
HP-UX )
bdf >> ${BASEDIR}/TABLE
;;
* )
df -v >> ${BASEDIR}/TABLE
;;
esac
echo "$LOGHOST is getting ready to bother you!" >> ${BASEDIR}/MAILER
echo >> ${BASEDIR}/MAILER
sed -e '1d' -e '/usr$/d' ${BASEDIR}/TABLE >> ${BASEDIR}/TABLE2
sed 's/%//g' ${BASEDIR}/TABLE2 > ${BASEDIR}/TABLE
cat ${BASEDIR}/TABLE |while read ONE TWO THREE FOUR FIVE SIX
do
case $OSVERSION in
HP-UX | SunOS | OpenBSD )
if [ $FIVE -gt $LIMIT ]
then
print "$SIX partition at ${FIVE}% capacity" >> ${BASEDIR}/MAILER
else
continue
fi
break;;
* )
if [ $SIX -gt $LIMIT ]
then
print "$ONE partition at ${SIX}% capacity" >> ${BASEDIR}/MAILER
else
continue
fi
break;;
esac
done
MAILCOUNT=`cat ${BASEDIR}/MAILER |wc -l`
if [ $MAILCOUNT -gt 2 ]
then
cat ${BASEDIR}/MAILER |mailx -s "$LOGHOST : Potential Paging Threat!" $MAILEES
fi
rm -f ${BASEDIR}/TABLE ${BASEDIR}/TABLE2 ${BASEDIR}/MAILER
, Mike
Wednesday, June 4, 2008
Shell Script To Monitor Disk Usage On Linux and Unix
Sunday, May 18, 2008
Doing Search And Replace In Multiple Files With Unix and Linux Perl - Easy Or Hard?
Hey There,
For this weeks "Lazy Sunday" post we're going to take a look at the versatility of Perl on Linux or Unix. While I'm fairly certain there's little argument that it's the best tool for most "extraction" and "reporting" functions when used in complicated situations, it's always been more interesting to me because of the wide range of ways you can complete any sort of task.
Today we're going to take a look at two different ways to do search and replace in multiple files using strictly Perl. The first way will be obnoxiously long and the second way will be almost invisible ;)
For both situations, we'll assume that we have 15 files all in the same directory. We'll also assume that we're logged into our favorite flavour of Linux or Unix OS and, coincidentally, in the same directory as those files. All the files are text files and are humungous. And, finally, all of the files are stories where the main character's name is Waldo, they've never been published and the writer's had a change of heart and decided to name his main character Humphrey. It could happen ;)
1. The hard way (or, if you prefer, the long way):
We'll write a script to read in each file and scour it, line by line. For lines on which the name Waldo appears, we'll replace that with Humphrey. We're taking into account, also, that Waldo may be named more than once on any particular line and that the name Waldo may have accidentally been mistyped with a leading lowercase "w," which needs to be corrected. That script would look something like this:#!/usr/bin/perl
#
# replace_waldo.pl - change Waldo to Humphrey in all files.
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
@all_files = `ls -1d *`;
$search_term = "Waldo";
$replace_term = "Humphrey";
foreach $file (@all_files) {
$got_one = 0;
chomp($file);
open(FILE, "<$file");
@file=<FILE>;
close(FILE);
foreach $line (@file) {
if ( $line =~ /Waldo/i ) {
$line =~ s/Waldo/Humphrey/gi;
$got_one = 1
}
}
if ( $got_one ) {
open(NEWFILE, ">$file.new");
print NEWFILE @file;
close(NEWFILE);
rename("$file.new", "$file");
}
}
2. The easy way (or, again, the short way):
Assuming the exact same convoluted situation, here's another way to do it (which we've covered in a bit more detail in this older post on using Perl like Sed or Awk):
From the command line we'll type:
host # perl -p -i -e 's/Waldo/Humphrey/gi' *
And we're done :)
Of course, the longer method is better suited for situations in which there are other extenuating circumstances. Or, perhaps, even more work to do. For the sort of limited situation we've laid out today, I will almost always go with the second method (Who wants pie? :)... Unless I have lots of time on my hands ;)
Cheers,
, Mike
Tuesday, April 1, 2008
Script For A Simple Menu Using Tput And ANSI Color

Click above to see a larger example of what happens when you dare to select option 1 ;)
Hey there,
Today I thought we'd look at something that almost all admins have to do at some point or another; create a simple shell menu script for users who aren't Unix or Linux savvy, but need to make use of the systems directly, nonetheless.
To spice it up (a little. There's only so much you can do ;) I've used colors within the standard shell menu framework, sticking with the ECMA compliant ANSI color codes. The reason I choose to stand by these color codes (since they, admittedly, lack the range of colors available in different flavors of Linux and Unix) is that they're virtually guaranteed to work on "all" flavors of Linux and Unix. Or maybe I'm just a people pleaser who hasn't realized that he can't win 'em all yet ;)
You'll notice also that, rather than just throw the menu together, using "case" inside a "select" loop, I went out of my way to complicate things by using tput to manage the cursor on the screen. In this case, it actually adds some functionality to the menu that would, otherwise, be impossible to implement. I shied away from unnecessarily using "getopts" when our "case" statement can handle these simple menu arguments just as efficiently :).
You can run this menu very simply, from the command line, like so:
host # ./SimpleMenu.sh
And there are only a few things to really take note of today. In a future post, I'll devote more time to utilizing your terminal screen with tput. It can be used to do a lot more useful, and entertaining, things than what we're having it do today.
Basically, in quick sequential order, we're using tput to:
1. Make the cursor invisible : tput civis
2. Position the cursor at various places on the terminal: tput cup x y <--- With x and y being single numeric coordinates designating the row and column, respectively.
3. Save the current cursor position: tput sc
4. Get back to (recover) the previously saved cursor position: tput rc
5. Make everything go back to the way it was, just in case our screen gets screwed up: tput reset
You can also, very simply, modify this shell menu script by just changing the menu options and adding your own routines to the case loop. And, if you can find something less offensive to your sensibilities, please do change the color scheme ;)
Enjoy :)
This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License#!/bin/bash
#
# SimpleMenu.sh - Only SomeWhat Useful ;)
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
trap 'tput reset;exit 1' 1 2 3 9 15
tput civis
while :
do
echo -e "\033[44;37;22m"
clear
echo -e "\033[41;37"
echo -ne "\033[46;30m"
tput cup 8 25 ; echo -n "The Linux and Unix Menagerie"
tput cup 9 25 ; echo -n " Shell Account Services "
tput cup 10 25 ; echo -n " W E L C O M E "
echo -e "\033[40;32m"
tput cup 12 25 ; echo -n " 1. See Who's Logged In "
tput cup 13 25 ; echo -n " 2. Reboot The Server "
tput cup 14 25 ; echo -n " 3. Gain Root Access "
tput cup 15 25 ; echo -n " 4. Call Your Mother "
tput cup 16 25 ; echo -n " 5. Quit "
echo -ne "\033[41;30m"
tput cup 18 28 ; echo -n " Pick Your Poison: "
tput cup 18 48
tput sc
read y
tput cup 20 30
case "$y" in
1|S|s)
output=`w|awk '{print $1}' |xargs -ibing grep bing /etc/passwd|awk -F":" '{print $5}'`
starting_line=21
tput cup $starting_line 28;
echo -ne "\033[46;30mCurrently Logged In Users:"
echo -ne "\033[0m"
echo "$output"|while read x
do
let starting_line=$starting_line+1
tput cup $starting_line 28;
echo -n $x
done
tput rc
echo -n "Any Key To Menu"
read x
;;
2|R|r)
tput cup $starting_line 28;
echo -n "Option Not Active Yet"
tput rc
echo -n "Any Key To Menu"
read x
;;
3|G|g)
tput cup $starting_line 28;
echo -n "Option Not Active Yet"
tput rc
echo -n "Any Key To Menu"
read x
;;
4|C|c)
tput cup $starting_line 28;
echo -n "Option Not Active Yet"
tput rc
echo -n "Any Key To Menu"
read x
;;
5|Q|q)
tput reset
clear
exit
;;
*)
tput cup 20 28
echo "$y - Unknown Option"
tput rc
echo -n "Any Key To Menu"
read x
;;
esac
done
, Mike
linux unix internet technology
Saturday, November 3, 2007
Using "case" Instead of "getopts" To Handle Script Input
Hey There,
I'm sure there are die-hard folks out there (like me), who prefer the getopts function to any other when it comes to parsing command line options for a nice shell script.
However, there are times when it makes more sense to use a "case" statement instead. One the most blatant examples can be seen in almost any init script on your Linux or Unix operating system. The simple rule is: If you're only accepting a very limited amount of input at the command line, there's no sense in over-complicating things. Not to mention the fact that "case" is more likely to port across systems and shells than getopts!
A "case" statement is very simple to write up. Let's take the example of the init script. It will generally accept about 3 arguments: start, stop or restart. Since these scripts are specifically for starting and stopping services, limiting to only those 3 command line options make sense and keeps the scripts brief, which is a benefit at boot-time.
Here's a sample "case" statement for a shell script that accepts the three above-mentioned arguments (only one of three, actually) and will return a usage error if it receives anything it doesn't expect:
case "$1" in
'start' )
/usr/local/bin/yourcommand start
;;
'stop' )
/usr/local/bin/yourcommand stop
;;
'restart' )
/usr/local/bin/yourcommand stop
/usr/local/bin/yourcommand start
;;
* )
echo "Usage: $0 [start|stop|restart]"
exit 1
;;
esac
Pretty simply, the "case" command iterates through the variable option it is told to act upon. Is this situation "$1" which translates, in the shell, to the first argument on the command line. It reads this in, translates it and then compares it to all of the available options it lists. Of course, it will match one of them, since our final option is "*" which is a special character which will match anything.
Note that the Usage output also utilizes a variable: $0. This equates to the name of the called command, exactly as it was called. So if you typed in "./myscript halt" you would get return output of: "Usage: ./myscript [stop|start|restart]" and be returned to the command line.
As you can see, in some cases (no pun intended) "case" makes a better case for utilization than getopts ;)
, Mike
linux unix internet technology

