Scrap your boilerplate; POSIX shell edition

I don't remember where I saw this first - probably in the Debian Installer code. Anyway, given an n-tuple it can be somewhat unwieldy to unpack it:

VAR="Aho Sethi Ullman"
ONE="$(echo ${VAR} | cut -d' ' -f1)"
TWO="$(echo ${VAR} | cut -d' ' -f2)"
THREE="$(echo ${VAR} | cut -d' ' -f3)"

The trick being presented is to generate the boilerplate code and then eval it:

VAR="Aho Sethi Ullman"
eval $(echo ${VAR} | awk '{ print "ONE=" $1 " TWO=" $2 " THREE=" $3 }')

Here's a more concrete example, with apologies to Python:

for PAIR in \
           "one    Aho" \
           "two    Sethi" \
           "three  Ullman"
    eval $(echo ${PAIR} | awk '{ print "k=" $1 " v=" $2 }')

    # .. do something with $k and $v

Comments (5)

nice trick, but it fails if the input data contains the same quotes you use to try to protect it from the eval

(quoting: number one reason to hate shell code)

Feb. 5, 2009, 1:55 a.m. #
It also fails at spaces and literal $'s in the source string but I'm trying to avoid the details. Did I read somewhere you wrote OOP extensions for sh?

Better yet:

VAR="Aho Sethi Ullman"
# do stuff with ${a[0]}, ${a[1]}, ${a[2]}

Feb. 5, 2009, 3:44 a.m. #
I hope it's obvious to everyone else why this "improvement" is not valid here.

In most of these cases, you ought to let the shell do the splitting, e.g.

set -- Aho Sethi Ullman
echo $3 $2 $1


while read k v; do something $k $v; done <<_eof
red green
blue yellow

Feb. 5, 2009, 6:03 a.m. #
Using `set` is nice (especially as you can change what you are splitting on with by altering IFS) but it unconditionally overwrites $1/$2, etc which makes me uncomfortable. And is multi-variable `read` actually POSIX?
Ed Davies

Here's something of the type which I did the other day:

pos -l8 $* | { read LAT LONG
echo Latitude = $LAT
echo Longitude = $LONG
# gnome-open "http://maps.go…"
gnome-open "http://www.ope…"

("pos" is my own little utility which converts various flavours of latitude/longitude or Ordnance Survey grid reference to various other flavours of the same.)

The "scope" of the result can be regarded as a PITA or quite elegant depending...

Feb. 5, 2009, 11:56 a.m. #

A bit off-topic here, but still... Yesterday I had to fiddle a bit with bash quoting and evaluation, as I am working on a specific-purpose system installer - The only user interaction I have is via this loop:

echo -n "$conf: (default = ${!conf}): "
read tempvar
if [ ! -z "$tempvar" ]
eval ${conf}=\$tempvar


I had to squeeze my brain (and bash's manpage) for the indirect expansion (${!conf}) and for the proper way for the assignment... It is still breakable, but tolerably so at least.

Feb. 5, 2009, 1:49 p.m. #