Shell programming style -- a plea for better shell scripts
Guido van Rossum
guido at mcvax.UUCP
Thu Feb 9 07:50:57 AEST 1984
Here are some rules for writing shell scripts in such a way that
they are more readable, robust and still not too slow.
1. If at all possible, use the Bourne shell (/bin/sh), not the C shell
(/bin/csh), even if your login shell is the latter. The Bourne shell
has a better way of treating multi-line control structures (if, for,
case etc.) and better substitution rules. Bourne shell scripts
are also easier to port to other sites than C shell scripts are.
(Exception: then C shell is superiour if lots of arithmetic must be done.)
2. Use blank lines, comments and indentation like you would in C or
Pascal programs.
3. Whenever parameter substitution (e.g., $1) or variable substitution
(such as $HOME) are used, decide whether there should be double quotes
("") around it. If it is expected that there may be embedded blanks
in the actual value (theoretically this can even occur in filenames) and
it is passed as a single argument to another program, quote it!
Also note the very useful difference between "$*" and "$@", which both
expand to the concatenation of all arguments. When passed to another
program, "$*" is always one argument; "$@" is as many arguments as
there were originally, if there was at least one. $* (without quotes)
omits empty or blank arguments (created by passing, e.g., "" as argument)
and splits arguments in thwo when they contain blanks.
(Note that "" passed as a file name means the current directory, which
is almost certainly not what was meant!)
E.g., to print all arguments, by default the standard input:
case $# in
0) print;;
*) print "$@";;
esac
4. Use "case" for string comparisons rather than "if". That is, to see
e.g. whether $1 equals "-a", use:
case "$1" in
-a) then-part;;
*) else-part;;
esac
rather than
if [ "$1" == "-a" ]; then
<then-part>
else
<else-part>
fi
The reason is mainly that "[" "]" executes as a separate process,
while the case is executed by the shell. (I know that some shell
derivates avoid the extra process in this case; but the vanilla
V7 Bourne shell is the subject of this article.)
5. Avoid the commands "true" and "false". They are implemented through
separate processes. The next paragraph shows an alternative for
"while true":
6. Be careful to design a parameter convention which mimics that of other
well-known Un*x programs; e.g. command [-flag] ... [file] ... .
If files can be given as arguments, the script should read its
standard in put instead. Example of how to program this:
while : # ":" is a built-in do-nothing command
do
case "$1" in
-a) <process-a-flag>; shift;;
-b) <process-b-flag>; shift;;
-*) echo "Usage: $0 [-a] [-b] [file] ..." 1>&2; exit 1;;
*) break;; # breaks out of loop
esac
done
And then proceed with the strategy pointed out in paragraph 3.
I could go on indefinitely with this, but try to stop here.
Any comments? Contrary visions? Other hints? (Flames?, I would add...)
Guido van Rossum
Centrum voor Wiskunde en Informatica, Amsterdam
...!{decvax,philabs}!mcvax!guido
More information about the Comp.unix
mailing list