2009-11-06
A shell script thing that I have learned the hard way
Here is a note to myself about shell scripting, especially things that I
write on the fly on the command line: under no circumstance should I use
any 'for ...
' loop index variable apart from $i
.
I say this because, based on experimental evidence so far, I am
completely sure to use $i
in the body of the for
loop, no matter
what sensible variable name I picked as the loop index in the for
statement. And, of course, the Bourne shell will let me shoot myself in
the foot this way, with little or no warning (depending on the commands
I'm running).
(This is where someone recommends 'set -u
'. The problem with this is
that some amount of my routine environment probably depends on being
able to 'expand' unset variables. I could turn it on and spend some
unknown amount of time cleaning things up, or I could remember to always
use $i
; so far, just using $i
is winning.)
The Bourne shell isn't the only language where it's not an error to use an undefined variable, of course. While it's tempting to say that this behavior is a bad idea, that's really treating the symptom and not the disease; my real mistake is using the wrong index variable, not using an undefined variable. Catching the symptom would only save me until the day when I write:
for i in ...; do ...; done
[ ... some amount of stuff ...]
for zp in ...; do frobnicate $i; done
The $i
variable will be perfectly well defined and my for
loop
will still go horribly wrong.
(I suspect that the usual general proposal to deal with this is to make loop index variables go out of scope at the end of loops.)
Sidebar: where this came from
Today's version of this lesson was writing:
for zp in $(<get list of ZFS pools>); do zpool export $i; zpool import $i; done
The goal was to clean up some ZFS pool state, but instead it did nothing at all (the
'zpool
' command doesn't complain if these options have no arguments)
and I spent quite some time trying to figure out why running the same
commands by hand on individual pools worked but the for
loop did
nothing.