Compact script with array - Good idea?

Hi,

I have a shell script where a lot of the code is repeated. I wanted to make the code much more compact so I spoke to a guy and he suggested using arrays, like follows:

#!/bin/bash

readonly -a nginx=('nginx' '--prefix=/opt'  '-j 4'
    'http://nginx.org/download/nginx-1.2.2.tar.gz' 
)
readonly -a supervisor=('supervisor' '--prefix=/opt'  '-j 4'
    'http://pypi.python.org/packages/source/s/supervisor/supervisor-3.0a12.tar.gz'
)

# Download, configure, build and install the given application array
#
# $1 is an application array
#
# Note: The four first lines are used to correctly get the array parameter
# - Setting the shell's Internal Field Separator to null
# - Create a string containing "array content"
# - assign app value to $1[*] using indirect variable reference
# - Resetting IFS to default
#
#
function install()
{
    OLD_IFS=$IFS; IFS=''; 
    local array_string="$1[*]" 
    local app=(${!array_string})
    IFS=$OLD_IFS

    echo "Installing '${app[0]}' from '${app[3]}' "
    echo "configure '${app[1]}'"
    echo "make '${app[2]}'"
}

install nginx
install supervisor

I like this, because not only is the code nice and short, but it stops repetition where operations are similar, for example downloading files.

Is there anything wrong with doing this?

It looks pretty, but I wouldn't call it organized. You've got lots of data duplication -- why bother storing nginx in an array named nginx -- and if both things get the same prefix and make options, why not split that out too? And you're not looping things you can loop. And you're desperately avoiding splitting when it's something that could replace 80% of your code here. And using arrays means you end up spitting out ${ARR[1]} and ${ARR[2]} everywhere instead of using meaningful variable names. Why is '-j 4' the third element of your array and not the second, fifth, ninth? Only the original programmer knows.

In short this code will be as hard for you to read later as it was for me to read now. :wink:

I'd still split the URL's out since they're big and unwieldy.

MAKE_OPTS="-j 4"
CONFIG_OPTS="--prefix=/opt"

nginx="http://nginx.org/download/nginx-1.2.2.tar.gz"
supervisor="http://pypi.python.org/packages/source/s/supervisor/supervisor-3.0a12.tar.gz"

for PACKAGE in nginx supervisor
do
        echo wget "${!PACKAGE}"
        echo configure $CONFIG_OPTS ... # Note not quoted
        echo make $MAKE_OPTS ... # Note not quoted
done

I suppose you could have variables overriding the default $CONFIG_OPTS and $MAKE_OPTS for a particular package. If they're blank, use the default options, if they're not, use them instead.

#!/bin/bash
# Requires bash, don't use other shells

MAKE_OPTS="-j 4"
CONFIG_OPTS="--prefix=/opt"

url_nginx="http://nginx.org/download/nginx-1.2.2.tar.gz"
nginx_MAKE="-j 3" # Override default MAKE_OPTS

url_supervisor="http://pypi.python.org/packages/source/s/supervisor/supervisor-3.0a12.tar.gz"
supervisor_CONFIG="--prefix=/usr" # Override default CONFIG_OPTS

for PACKAGE in nginx supervisor
do
        # Check for package_MAKE variable, use default $MAKE_OPTS if absent
        MOPTS="${PACKAGE}_MAKE"
        MOPTS="${!MOPTS:-${MAKE_OPTS}}"
        # Check for package_CONFIG variable, use default $CONFIG_OPTS if absent
        COPTS="${PACKAGE}_CONFIG"
        COPTS="${!COPTS:-${CONFIG_OPTS}}"

        PACKAGE="url_${PACKAGE}"
        echo wget "${!PACKAGE}"
        echo configure $COPTS ... # Note not quoted
        echo make $MOPTS ... # Note not quoted
done