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"
do
    eval $(echo ${PAIR} | awk '{ print "k=" $1 " v=" $2 }')

    # .. do something with $k and $v
done

§

Tags: Hacks
Planets: ALUG UWCS WUGLUG Debian

§

Five comments

  1. 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)

    Joey Hess

    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?

    lamby

  2. Better yet:

    #!/bin/bash
    ...
    VAR="Aho Sethi Ullman"
    ...
    a=($VAR)
    # do stuff with ${a[0]}, ${a[1]}, ${a[2]}

    Anonymous

    I hope it's obvious to everyone else why this "improvement" is not valid here.

    lamby

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

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

    or

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

    madduck

    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?

    lamby

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

    #!/bin/bash
    pos -l8 $* | { read LAT LONG
    echo Latitude = $LAT
    echo Longitude = $LONG
    # gnome-open "http://maps.google.com/?ie=UTF8&ll=${LAT},${LONG}&spn=0.003117,0.009656&z=17"
    gnome-open "http://www.openstreetmap.org/?lat=${LAT}&lon=${LONG}&zoom=18&layers=B000FTF"
    }

    ("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...

    Ed Davies

  5. 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:

    for conf in HD CDR SWAP_SIZE CONF_URL
    do
    echo -n "$conf: (default = ${!conf}): "
    read tempvar
    if [ ! -z "$tempvar" ]
    then
    eval ${conf}=\$tempvar
    fi

    done

    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.

    Gunnar

Reply

§