I ran into something today that seems so very basic that I’m taken aback it hasn’t bitten me before.
For example, take this simple BASH ‘for’ loop:
for DERP in /dev/tty*; do echo ${DERP}; done
This should return a list of all the files matching /dev/tty* since BASH’s built-in file globbing expands as the list parameter of ‘for’ . Now, try this:
[bdowney@tlfmgt1 ~]$ for DERP in /dev/heraderp*; do echo $DERP; done /dev/heraderp*
Now the ${DERP} variable expanded to “/dev/herpaderp*”, but why? The answer is that since /dev/herpaderp* did not naturally expand into a list of matches (because none matched) the pattern itself becomes the list parameter. Makes sense once you understand what is happening, but seems a bit counterintuitive while programming.
I think most shell programmers would assume (myself included) the for loop would execute zero times upon no glob match.
So it would seem the correct way to handle this is by executing a subshell with a command that properly handles no-match globs, namely “ls”. In this case, it works perfectly and as expected–but we have yet another caveat:
for DERP in $(ls /home/bdowney/*.mp3 2>/dev/null); do echo $DERP; done
If we run into a file name with a space, say something like “01 – track1.mp3″, this version of the loop will still enumerate down to three separate list items because of the spaces, namely “01″, “-”, and “track1.mp3″. We cannot escape the subshell with double quotes, either. So we’re back to the plain globbing again without the subshell. So how do we resolve this? As follows:
for DERP in /dev/tty*; do
[[ -f ${DERP} ]] || continue
echo $DERP
done
Just test the loop iteration to see if it matches a file. Problem solved in all situations!

Recent Comments