Indirect reference to hashmap in bash without nameref

59 Views Asked by At

This is the same situation as a question I asked about a year ago, however I am now targeting RHEL7 and Bash 4.2.46. I am writing scripts to install packages from archive files, checking if the package is already installed first. I have the information for each package file in a config file like so:

### packages.config ###
declare -A package_a=([name]="utility-blah" [ver]="2.4.1")
declare -A package_b=([name]="tool-bleh" [ver]="1.3.9")
# and so on and so forth

My various install scripts source this config file to get package information. Each script contains an array of the packages to be installed. I would like to iterate over this array and install the package named. Ideally, the meat of this loop is in some function so I don't have to rewrite it in each script. In Ubuntu 18 with Bash 4.4, I was able to include this function in the config file like so:

### functions.config ###
function install_package()
{
  # dereference hashmap variable
  declare -n package="$1"
  if dpkg --list ${package[name]}; then
    echo "${package[name]} is installed"
  else
    dpkg -i $pathToPackages/${package[name]}_${package[ver]}.deb
  fi
}

This leaves only the loop for the actual script, which looks like this:

### install.sh ###
source packages.config
source functions.config
declare -a packageList=("package_a" "package_b" "package_d")
for package in ${packageList[@]}; do
  install_package $package
done

This worked perfectly for me on Bash 4.4. Unfortunately, the declare -n option was added in Bash 4.3, and as I am now targeting Bash 4.2, the nameref solution no longer works. Is there a way I can have this same behavior where the script will dereference the package variable? I've tried using indirection as this post suggests, but I don't think ${!package[name]} works the way I want it to. Is it possible to achieve the same functionality without using declare -n?

3

There are 3 best solutions below

0
that other guy On

Add the index to your indirection variable:

$ declare -A package_a=([name]="utility-blah" [ver]="2.4.1")
$ var="package_a[ver]"
$ echo "${!var}"
2.4.1

This works on Bash 4.2.45(1)

0
Philippe On

Can this achieve what you expected ?

declare -A packages=([package_a]="utility-blah:2.4.1"
                     [package_b]="tool-bleh:1.3.9")

function install_package {
  declare package="${packages[$1]}"
  declare name="${package%:*}" version="${package#*:}"
  if dpkg --list $name; then
    echo "$name is installed"
  else
    dpkg -i $pathToPackages/${name}_$version.deb
  fi
}
3
bobe On

I was able to solve the problem by applying the solution from this question. My script is unchanged, I just changed the functions.config file like so:

### functions.config ###
function dereference(){
    local v=$1[$2]
    printf -- %s "${!v}"
}

function install_package()
{
    declare -A package=(
        [name]=$(dereference $1 name)
        [ver]=$(dereference $1 ver)
    )
    if rpm -q ${package[name]}>/dev/null; then
        echo "${package[name]} is installed"
    else
        echo "${package[name]} is not installed. Installing now..."
        rpm -i $current_dir/packages/${package[name]}-${package[ver]}.rpm
    fi
}