When you write a bash script, there's a few things you can do to make your life (and anyone else who uses it) a lot better. Here's a few of the things that I (try) to do when I write scripts to make it easier to use and less error prone.
#1 Comments
Seriously. Write them. A lot. Explain what you're doing so that when you come back 6 months later to see what the 'makestuff.sh' script does, at least you can figure out what you were doing from the comments as well as the commands.
#2 Filenames
Don't name your scripts 'makestuff.sh'. Even if you think you're only going to use it once, name it something good. Same goes for variables in the script.
#3 USAGE="..."
Always have a USAGE variable and echo it out when the script is called with -h. If your script requires arguments, print it when none are supplied. The bonus is, it's like a comment for the whole script. Put it at the top and when you open it later to see what it does, you'll smile knowing you could have just used the -h flag. It's also handy if you always forget what options and arguments you need.
Here's the simplest way to do it:
#!/bin/bash
USAGE="
$(basename $0) usage:
$(basename $0) arg1 [arg2 ...]
Basic description of script functionality goes here
"
if [ "$1" == "-h" ]; then
echo "$USAGE"
exit
fi
# rest of the script goes here
If your script needs a minimum number of arguments, why not print it when too few are supplied?
if [ $# -lt 1 ]; then
>&2 echo "$USAGE" # echo to stderr
exit 1 # exit with error code
fi
See below for how to do this with option parsing
#4 `set -u' and `set -e'
David Pashley has a great article called Writing Robust Bash Scripts where he outlines many good practices when writing scripts. Using set -u
and set -e
are just two of the many great suggestions he has.
set -u
tells the shell to abort if you try to use a variable that wasn't set. Seems kinda drastic, but this will save you a lot of headaches when an unset variable is evaluating empty just because you spelled it wrong or forgot to initialize it.
set -e
tells the shell to abort if any command fails. I like this one especially for scripts that do a lot or rm
ing or other dangerous stuff. If something breaks before the script gets to the dangerous part, I want it to stop.. like now!
#5 Option parsing
Okay, so now we're getting into the fun stuff.. Option parsing is not all that difficult in bash and I didn't do it for a long time. Until I came across this stackoverflow answer.
Here's a generalized version of the answer in the link:
#!/bin/bash
# see http://stackoverflow.com/a/14203146 for more on this
set -e
set -u
###################
# Option Defailts #
###################
opt="option default"
flag=false
verbose=0
###################
# Version & Usage #
###################
VERSION="0.0.0"
USAGE="
$(basename $0) version $VERSION
usage:
$(basename $0) [options] arg1 [arg2 ...]
options:
-o, --opt val set opt to val [$opt]
-f, --flag enable flag
-v, --verbose increase verbosity
-h, --help print this message and exit
-- options terminator
DESCRIPTION
"
#################
# Parse Options #
#################
while [[ $1 == -* ]]; do
case $1 in
-o|--opt)
opt="$2"
shift # past value
;;
-f|--flag)
flag=true
# do not shift again!
;;
-v|--verbose)
((verbose+=1))
# do not shift again!
;;
-h|--help)
echo "$USAGE"
exit
;;
--)
shift
break
;;
*)
>&2 echo "$(basename $0): unknown option $1" \
"(run '$(basename $0) --help' for available options)"
exit 1
esac
shift
done
# all options have been shifted out, so just arguments are left in $@
###############
# Main Script #
###############
echo "opt = $opt"
if [ "$flag" = true ]; then
echo "flag = enabled ($flag)"
else
echo "flag = disabled ($flag)"
fi
echo "verbose = $verbose"
i=0
for arg in "$@"; do
echo "arg $i = $arg"
((i+=1))
done
Additionally, you can define some helper functions to echo commands given the verbosity level:
error() { >&2 echo $*; } # always prints to stderr
warn() { (($verbose>0)) && echo $* || true; } # prints when verb is >0
info() { (($verbose>1)) && echo $* || true; } # prints when verb is >1
debug() { (($verbose>2)) && echo $* || true; } # prints when verb is >2
verbose() { (($verbose>3)) && echo $* || true; } # prints when verb is >3
No comments :
Post a Comment