My common patterns in shell script verbosity (for sysadmin programs)
As a system administrator, I wind up writing a fair number of scripts that exist to automate or encapsulate some underlying command or set of commands. This is the pattern of shell scripts as wrapper scripts or driver scripts, where you could issue the real commands by hand but it's too annoying (or too open to mistakes). In this sort of script, I've wound up generally wanting one of three different modes for verbosity; let's call them 'quiet', 'dryrun', and 'verbose' In 'verbose' mode the script runs the underlying commands and reports what exactly it's running, in 'quiet' mode the script runs the underlying commands but doesn't report them, and in 'dryrun' mode the script reports the commands but doesn't run them.
Unfortunately this three-way setup is surprisingly hard to implement in a non-annoying way in Bourne shell scripts. Now that I've overcome some superstition I wind up writing something that looks like this:
run() { [ "$verb" != 0 ] && echo "+ $*" [ "$DOIT" = y ] && "$@" } [...] run prog1 some arguments run prog2 other arguments [...]
This works for simple commands, but it doesn't work for pipelines
and especially for redirections (except sometimes redirection of
standard input). It's also somewhat misleading about the actual
arguments if you have arguments with spaces in them; if I think I'm
likely to, I need a more complicated thing than just 'echo
'.
For those sort of more complicated commands, I usually wind up having to do some variant of this code as an inline snippet of shell code, often writing the verbose report of what will be run a little bit differently than what will actually get run to be clear about what's going on. The problem with this is not just the duplication; it's also the possibility of errors creeping into any particular version of the snippet. But, unfortunately, I have yet to come up with a better solution in general.
One hacky workaround for all of this is to make the shell script
generate and print out the commands instead of actually trying to
run them. This delegates all of the choices to the person running
the script; if they just want to see the commands that would be
run, they run the script, while if they actually want to run the
commands they feed the script's output into either 'sh -e
' or
'sh -ex
' depending on whether they want verbosity. However, this
only really works well if there's no conditional logic that needs
to be checked while the commands are running. The moment the generated
commands need to include 'if
' checks and so on, things will get
complicated and harder to follow.
|
|