26 February 2007

Option-ize your shell scripts

If you're like me you may have started shell scripting very simply, maybe just to collect a number of other unwieldy commands into one executable file to call with just one short command (the name of the script). Over time (hopefully!), you will have discovered many of the other useful features of shells, like variables, redirections, and conditional expressions. As one's scripts become more complicated, one mistake a person might make is to copy and then modify a script to do something that's really only a little bit different. A better way to handle this may be to keep it all in one script and just use command line options to that script. Once you start using options to your scripts (as opposed to just arguments), all sorts of great possibilities open up.

I'm using the bash shell here and this probably won't work the same way with most other shells. Below, I've put an options demo' script. If you want to give it a try, you should have bash installed and then just paste the below into a file and 'chmod' it to be executable. This system of using options with your scripts will allow boolean options, defaults with option overrides, options with arguments, options in a blob (e.g., -aBbdg5) or singly, some crude syntax checking of the options, and required arguments as well. So, first the script, then some example output, and finally some discussion of the nuts and bolts for the curious.
#!/usr/bin/env bash
USAGE='usage: '`basename $0`' [-13ds] [-o out-file] filename'
snmpver=2c
sync=0
debug=0
while [[ ${1:0:1} = '-' ]] ; do
N=1
L=${#1}
while [[ $N -lt $L ]] ; do
case ${1:$N:1} in
'd') debug=1 ;;
's') sync=1 ;;
'1') snmpver=1 ;;
'3') snmpver=3 ;;
'o') if [[ $N -ne $(($L-1)) || ! -n ${2} ]] ; then
echo $USAGE
exit 1
fi
outfile=${2}
shift ;;
*) echo $USAGE
exit 1 ;;
esac
N=$(($N+1))
done
shift
done
if [[ ! -n ${1} ]] ; then
echo $USAGE
exit 1
fi
infile=$1
echo -n "snmpver:$snmpver debug:$debug sync:$sync "
echo "outfile:$outfile infile:$infile"
I'd normally put more white space in a script, but I want to keep it tight here. Some example output with short comments:
$ optionize.bsh
usage: optionize.bsh [-13ds] [-o ] filename
$ #that last argument _is_ required
$
$ optionize.bsh blah
snmpver:2c debug:0 sync:0 outfile: infile:blah
$ #defaults
$
$ optionize.bsh -s1 -o yada blah
snmpver:1 debug:0 sync:1 outfile:yada infile:blah
$ #blobs are fine
$
$ optionize.bsh -sd1 -o yada -3 blah
snmpver:3 debug:1 sync:1 outfile:yada infile:blah
$ #last option on the command line overrides any previous
$
$ optionize.bsh -s -d -d1o yada -3o Yadaya blah
snmpver:3 debug:1 sync:1 outfile:Yadaya infile:blah
$ #options requiring arg.s can be in a blob, but ...
$
$ optionize.bsh -d -so1 yada blah
usage: optionize.bsh [-13ds] [-o ] filename
$ # ... not in the middle of a blob, obviously.
How this works is really pretty simple if you're familiar with bash's positional parameters and shell parameter expansions. For a script called from the command line like this, the positional parameters will be each of the arguments numbered from left to right, from 1 to the number of arguments. In my first while loop, each time I finished processing an option I called the shell command 'shift', which pops off that first positional parameter and shifts the remaining ones down one position each. In the case of the '-o' option I first checked that it wasn't stuck in the middle of an options blob, then I took the following positional parameter ("$2") as the value for the option "outfile" and performed an extra shift. BTW, I was thinking here that if "outfile" was not specified that we'd just send the output to standard out, but you could just as easily have specified a default out file.

The other confusing part might be the shell parameter expansions. Briefly, "${1:0:1}" is using bash substring expansion and the syntax is "${parameter:offset:length}". So, it's short for the first character of positional parameter 1 (yes, it is zero-indexed). Similarly, "${1:$N:1}" is taking one character at a time from an option blob. And, "${#1}" is short for the number of characters in positional parameter 1.

(Can you see which variable in the script wasn't necessary? :) )

If you want to learn more about these parameter expansions, or bash in general, look here: Shell Parameter Expansion.

Labels:

24 February 2007

Enabling your mouse scroll wheel in X

This is a quick tip. It's been around for a while and I'm not even sure where I first saw it. But, if you have a mouse with a roller wheel, you can add this one line option to your X configuration file to enable it. You'll then be able to use it to scroll up and down in your web browser, terminal windows, pretty much any program that has an "up" and "down."

I'm using xorg on my systems now, but I believe the scroll-wheel option was exactly the same in XFree86. In the block for you mouse, which typically looks like this:
Section "InputDevice"
Identifier "Mouse1"
Driver "mouse"
Option "Device" "/dev/sysmouse"
... just add one more line like this:
Option "ZAxisMapping" "4 5"
... restart X and you should be in business.


Whenever the subject is mice, I like to "give the devil his due" and admit that I use
M!cros0ft mice almost exclusively. That's one thing they've always done right. Software, not so much. :) But, a good, plain, optical mouse with a scroll wheel is hard to beat.

Labels: ,