Paranoia, and good programming practices

First, a bit of good programming practice:

Comment your code.

You really should at MINIMUM have some comment about every page (that's every 24 lines).
Ideally, you should always comment all your functions. One-line functions can probably stand by themselves, but otherwise, a quick single line comment is a good thing to have for small functions.


# This function cleans up the file before printing
pcleanup(){
	....
}

For longer functions, you should really use formal comment spec. Something like


# Function xyz
#  Usage: xyz arg1 arg2 arg3 arg4
#    arg1 is the device
#    arg2 is a file
#    arg3 is how badly you want to mangle it
#    arg4 is an optional output file
#  Result: the file will be transferred to the device, with the
#    appropriate ioctls. Any err messages will be saved in the output
#    file, or to stderr otherwise
xyz(){
	...
}
Note that shellscripts are themselves one large "function". So dont forget basic comments on your shellscript's functionality at the top of the file!

INDENT!

Every time you start a function, indent.

Every time you are in a while loop, or if clause, indent.

This makes it easier to see at a glance what level you are at.


# top level
print this is the top

somefunction(){
	# indent in functions
	print we are in the function now
	
	if [[ "$1" != "" ] ; then
		# yes, ANOTHER indent!
		print "Hey, we have an argument: $1"
		print full args are $*
	else
		# indent again!!
		print "oh well. no arguments to play with"
	fi

	print leaving somefunction now
}

# And now we can clearly see that all this stuff is outside any function.
# This makes it easier to find the "main line" of the script
print original shellscript args are $0
print lets try somefunction
somefunction heres some args

Error checking

If you are using scripts to check on important systems, or perform crucial functions, it is very important to provide feedback to the calling process when and if something goes wrong.

The simplest method of doing this, is a simple

   exit 1
   # (but it would be nice to print out SOME message before exiting!)
Nice programs will notice that your script exited with a non-zero status. [Remember, the status of the last command is in '$?']
Ideally, they will complain.
On the other hand, sometimes your own scripts are the ones that are doing the calling!

In that type of situation, it may be suitable to have a top-level script that keeps an eye on things. A simple example is:


fatal(){
	# Something went horribly wrong.
	# print out an errormessage if provided, then exit with an
	# "error" status

	if [[ "$1" != "" ]] ; then
		print Something went horribly wrong with "$1"
	else
		print "something went horribly wrong (Somewhere?)"
	fi

	# You mail also want to SEND EMAIL, or trigger a PAGER or
	# something here:
	# mailx -s "Arrrg! we failed checks!" admin@yourhost.com < /dev/null
	#
	exit 1
}

check_on_modems
if [[ $? -ne 0 ]] ; then 	fatal modems ; fi

check_on_network
if [[ $? -ne 0 ]] ; then	fatal network ; fi

check_on_servers
if [[ $? -ne 0 ]] ; then 	fatal servers ; fi


Note that even my paranoid 'fatal' function, IS PARANOID!
Normally, it is assumed you will call it with "fatal what-failed". But if you somehow dont, it notices, and provides a default.

Sometimes, making the assumption that $1 contains valid data can completely screw up the rest of your function or script. So if it is an important function, assume nothing!

This is particularly true of CGI scripts. [Gasp]. Yes, Virginia, it IS possible to write CGI in something other than perl.


cron job paranoia

If you are coding a script that specifically is in a cronjob, there are two things to be aware of. The number one most important thing is;

Set your PATH variable!!!

People always forget this one. It doesnt matter what is in your .profile, cron will reset the PATH variable to something really short and stupid, like just /usr/bin. So set your PATH=/whatever:/usr/bin explicitly in your cron scripts.

The second tip is more an advisory note.

cron by default saves anything that gets send to 'stderr', and MAILS IT to the owner of the cron job. So, sometimes, if you just want a minor error logged somewhere, it is sufficient to just do

print "Hey, this is kinda wierd" >/dev/fd/2
which will send the output of the print to stderr, which will then go to email. Another way of doing it, if your system does not have /dev/fd, could be
print "Wow, this is tortured" 1>&2

Contrariwise, if you want to throw away ALL output, use

command >/dev/null 2>&1

If you do not regularly read email for the user in question, you can either set up an alias for that user, to forward all its email to you, or do

export MAILTO=my@address.here

The MAILTO trick does not work on all cron demons, however.


TOP of tutorial
Next: Real code: A useful program done wrong, and then done right. Prev: Interesting stuff
This material is copyrighted by Philip Brown© January 2002-2012