[Gllug] bash indirect variable referencing ugliness

Nix nix at esperi.org.uk
Fri Nov 24 00:55:54 UTC 2006


On 21 Nov 2006, Tethys said:
> Errr... yes. Just get rid of them all. They're not necessary:
>
> 	adapter="adapter_$service"
> 	lnb="lnb_$service"
> 	szapout="szapout_$service"
> 	pid="pid_$service"
> 	ip="ip_$service"
>
> 	echo "Config values for service $service:"
> 	for value in adapter lnb_settings szapout pid ip
> 	do
> 		varname="${value}_$service"
> 		echo "  $value: ${!varname}"
> 	done

Bah, far too sane.

I present something in ksh (probably working on any POSIX shell) which
does the same thing with knobs on and should really *really* not be
applied to untrusted data:

#
# expand_variable VARIABLE
#
# Expand a VARIABLE by repeatedly evaluating it until it is entirely
# expanded. This handles variables that point to other variables,
# variables whose names are composed of the values of one or many other
# variables, and so forth. It is not fast, but it is flexible.
# This permits the use of variable constructs like ${FOO_${BAR}_BAZ},
# which are otherwise illegal in POSIX shells.
#
# This is fairly slow.

expand_variable()
{
    # All variable names start with an underscore; because we are expanding
    # the caller's variables here we do not want our own variable names to
    # collide with the caller's.

    typeset _VARIABLE="$1"

    while :; do
        # Multiple sets of variables in succession confuse the nested expander's
        # regexps, and cannot be dealt with in there, as picking out distinct
        # levels of variables requires matching brackets, and this cannot be
        # done by regular expressions. So we pull the start off the variable,
        # and if we need to, we recursively expand the end of the variable until
        # only a starting piece is left. So `${FOO${BAR}}${BAR${BAZ}}quux' is
        # cut into two pieces, and `${BAR${BAZ}}quux' is handled by a recursive
        # call.

        # First, cut the variable into starting and ending pieces, with a simple
        # awk bracket matcher, modified to give everything if no braces are
        # present at all.

        typeset _VPBEGIN=`echo $_VARIABLE |\
            awk '
            {
                braces=0;
                if ((i=index($0,"{"))==0) {
                    print $0;
                }
                do {
                    if (substr ($0,i,1)=="{") {
                        braces++;
                    } else if (substr ($0,i,1)=="}") {
                        braces--;
                    }
                    ++i;
                } while (i<=length ($0) && braces!=0);
            print substr($0,1,i-1);
            }'`

        typeset _VPEND=`echo $_VARIABLE | sed "s/^$_VPBEGIN//"`

        # If the ending piece has any variable expansions in it, expand them.

        (echo "$_VPEND" | grep -F '${' > /dev/null 2>&1) && \
            _VARIABLE="$_VPBEGIN"`expand_variable "$_VPEND"`

        # Handle nested and other expansion, and the base case.

        case $_VARIABLE in
         # Nested expansion. Cut the variable name up and expand the middle bit.
         # This set of regular expressions should perhaps win some kind of award
         # for unreadability; matching ${} constructions is not pretty.
         *\${*\${*}*}*)
             typeset _VPBEGIN=`echo $_VARIABLE | sed 's/\(^[^\$]*\${[^\$]*\).*$/\1/'`
             typeset _VPMIDDLE=`echo $_VARIABLE | sed 's/^[^\$]*\${[^\$]*\(\${.*}\)[^}]*}[^}]*$/\1/'`
             typeset _VPEND=`echo $_VARIABLE | sed 's/^.*}\([^}]*}[^}]*\)$/\1/'`
             _VARIABLE="$_VPBEGIN"`expand_variable "$_VPMIDDLE"`"$_VPEND";;
         # Conventional expansion.
         *\$*) _VARIABLE=`eval echo $_VARIABLE`;;
         # Expansions completed.
         *) break;;
        esac
    done

    echo "$_VARIABLE"
}

-- 
`The main high-level difference between Emacs and (say) UNIX, Windows,
 or BeOS... is that Emacs boots quicker.' --- PdS
-------------- next part --------------
-- 
Gllug mailing list  -  Gllug at gllug.org.uk
http://lists.gllug.org.uk/mailman/listinfo/gllug


More information about the GLLUG mailing list