3

I am trying to pass an associative array from one function to another, and am losing named index keys (e.g., filepath, search in example below) though the array passed in can access its elements correctly using indexes 0, 1. I must be doing something slightly wrong with bash syntax, but can't quite figure out where. Any help appreciated.

Using GNU bash, version 4.3.8 from Ubuntu 14.04 Just below is bash code example, and at bottom is output

#! /bin/bash

function test_function {

    func_data=("${@}")
    # without brackets above cannot access func_data[1]

    # local ${!func_data}
    # the above local statement does not seem to help either way

    echo ""
    for K in "${!func_data[@]}"; do echo $K; done

    echo ""
    echo "func_data           : ${func_data}"
    echo "func_data[filepath] : ${func_data[filepath]}"
    echo "func_data[search]   : ${func_data[search]}"
    # all three echos above output first element of array, 
    # which is 'style "default" {' during first loop

    # Can access array elements 0, 1 but no longer via filepath, search
    echo "func_data[0]        : ${func_data[0]}"
    echo "func_data[1]        : ${func_data[1]}"

    echo "!func_data[@]       : ${!func_data[@]}"
    # echo above outputs '0 1' so indexes now are now zero based?
    echo "func_data[@]        : ${func_data[@]}"
    # echo above outputs all array elements 'style "default" { ~/.gtkrc-2.0'
}

# In BASH, local variable scope is the current function and every 
# child function called from it, so provide a function main to
# make it possible to utilize variable scope to fix issues

function main {

    echo ""
    declare -A gtkrc2=()
    gtkrc2[filepath]="~/.gtkrc-2.0"
    gtkrc2[search]="style \"default\" {"
    echo "gtkrc2 filepath : ${gtkrc2[filepath]}"
    echo "gtkrc2 search   : ${gtkrc2[search]}"
    test_function "${gtkrc2[@]}"

    echo ""
    declare -A gtkcss=()
    gtkcss[filepath]="~/.config/gtk-3.0/gtk.css"
    gtkcss[search]=".scrollbar {"
    echo "gtkcss filepath : ${gtkcss[filepath]}"
    echo "gtkcss search   : ${gtkcss[search]}"
    test_function "${gtkcss[@]}"
}

main

---------- OUTPUT ----------

gtkrc2 filepath : ~/.gtkrc-2.0
gtkrc2 search   : style "default" {

func_data           : style "default" {
func_data[filepath] : style "default" {
func_data[search]   : style "default" {
func_data[0]        : style "default" {
func_data[1]        : ~/.gtkrc-2.0
!func_data[@]       : 0 1
func_data[@]        : style "default" { ~/.gtkrc-2.0

gtkcss filepath : ~/.config/gtk-3.0/gtk.css
gtkcss search   : .scrollbar {

func_data           : .scrollbar {
func_data[filepath] : .scrollbar {
func_data[search]   : .scrollbar {
func_data[0]        : .scrollbar {
func_data[1]        : ~/.config/gtk-3.0/gtk.css
!func_data[@]       : 0 1
func_data[@]        : .scrollbar { ~/.config/gtk-3.0/gtk.css
4
  • 1
    By passing "${gtkrc2[@]}", you are effectively copying the elements of the array (without the indices) to a list, which then gets turned into a non-associative array when you do func_data=("${@}"). I'm thinking how you might solve this now... Commented Apr 25, 2014 at 23:57
  • Believe me when I say that * was part of my equations, BUT It never made no difference in anything I ever created. I knew, but had no syntax! Commented Apr 26, 2014 at 3:50
  • Sometimes, I think the purpose of adding associative arrays to bash was to help convince people that no, really, it's time to move your program to a more suitable language. bash is not meant for data processing. Commented Apr 26, 2014 at 12:29
  • Bash is part of my resume and maybe that means I know something about its 4.x limitations. Python 3.4 is my preferred language. Commented Apr 26, 2014 at 20:29

1 Answer 1

1

This may or may not be the "correct" way to do this but this is the best I can figure out. Any suggestions from others are welcome:

function test_function {
    arrname=$1
    idxlist="$2"

    echo ""
    echo "Array passed=$arrname"
    for idx in $idxlist; do
        elemname=$arrname[$idx]
        echo "idx=$idx, elem=${!elemname}"

    done
}

# In BASH, local variable scope is the current function and every 
# child function called from it, so provide a function main to
# make it possible to utilize variable scope to fix issues

function main {

    echo ""
    declare -A gtkrc2=()
    gtkrc2[filepath]="~/.gtkrc-2.0"
    gtkrc2[search]="style \"default\" {"
    echo "gtkrc2 filepath : ${gtkrc2[filepath]}"
    echo "gtkrc2 search   : ${gtkrc2[search]}"
    test_function gtkrc2 "${!gtkrc2[*]}"

    echo ""
    declare -A gtkcss=()
    gtkcss[filepath]="~/.config/gtk-3.0/gtk.css"
    gtkcss[search]=".scrollbar {"
    echo "gtkcss filepath : ${gtkcss[filepath]}"
    echo "gtkcss search   : ${gtkcss[search]}"
    test_function gtkcss "${!gtkcss[*]}"
}

main

In particular:

  • To pass each associative array to the function, we pass both the name of the array, and its list of indices
  • Inside the function, the array name and index list are taken from the positional parameters
  • We may then loop over the list of indices and obtain the corresponding value of each element. This is done by first generating the name of the element, and then using the ! indirection modifier to get the actual value.

This technique of indirection of arrays is described in this question, but only addresses indexed arrays, and not associative arrays; passing the index list is one way I can think of to get this to work for associative arrays.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks man! I bailed when having to even contemplate bash's $1 $2. I am a Python programmer, and I guess it's all pretty much the same but BASH will migraine with eccentricities. Much appreciated. Maybe obvious but I respect convention, Why? done, and rockin
@BradSturtevant Yep Bash is a language of idiosyncrasies, with the occasional convention thrown in to surprise unwary users. Having said that, I think it capable of more than many give it credit.
@DigialTrauma I am still having difficulty accessing a named value even with $1 and $2 in hand. The two statements 'search=$arrname[search] echo "search $search"' returns 'search gtkrc2[search]' and two statements 'search=$arrname[${idxlist[0]}] echo "search $search"' returns 'search gtkrc2[search filepath]' I think that maybe I might switch to regular indexes at this point!
Perhaps my example is a bit confusing. The key thing is that you need to use the ! modifier to access the array element value itself. If the name of the array element is in the $search as per your comment, then you will need to access its value using ${!search}.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.