'How to export an associative array (hash) in bash?

Related, but not a duplicate of: How to define hash tables in Bash?

I can define and use a bash hash, but I am unable to export it, even with the -x flag. For example, the following works to export (and test exportation of) a normal string variable:

aschirma@graphics9-lnx:/$ export animal_cow="moo"
aschirma@graphics9-lnx:/$ bash -c "echo \$animal_cow"
moo
aschirma@graphics9-lnx:/$ 

However, if I try to export a hash:

aschirma@graphics9-lnx:/$ declare -A -x animals
aschirma@graphics9-lnx:/$ animals[duck]="quack"
aschirma@graphics9-lnx:/$ echo ${animals[duck]}
quack
aschirma@graphics9-lnx:/$ bash -c "echo \${animals[duck]}"

aschirma@graphics9-lnx:/$ 

It seems the nested bash shell does not have the hash in its scope. I did verify this also by manually entering the nested bash shell and attempting to use the hash interactively.



Solution 1:[1]

There isn't really a good way to encode an array variable into the environment. See http://www.mail-archive.com/[email protected]/msg01774.html (Chet Ramey is the maintainer of bash)

Solution 2:[2]

As a workaround for this harsh Bash limitation I'm using "serialize to temporary file" method. You can export plain variables, so you can pass an array (associative) through filepath. Of course, this has limitations, but sometimes works and is good enough.

declare -A MAP # export associative array                                                                           
MAP[bar]="baz"                                                                        
declare -x serialized_array=$(mktemp) # create temporary variable 
# declare -p can be used to dump the definition 
# of a variable as shell code ready to be interpreted                                       
declare -p MAP > "${serialized_array}" # serialize an array in temporary file 

# perform cleanup after finishing script                                      
cleanup() {                                                                   
  rm "${serialized_array}"                                                    
}                                                                             
trap cleanup EXIT   

# ... in place where you need this variable ...
source "${serialized_array}" # deserialize an array                         
echo "map: ${MAP[@]}" 

Solution 3:[3]

short answer --> export animals after declaring it

full --> Try this way as a script:

#!/usr/bin/env bash

declare -A -x animals
export animals
animals[duck]="quack"
echo ${animals[duck]}
bash -c "echo ${animals[duck]}"

Output on my side using Bash version: 5.1.16(1)

quack
quack

or in terminal:

$ declare -A -x animals
$ export animals
$ animals[duck]="quack"
$ echo ${animals[duck]}
quack
$ bash -c "echo ${animals[duck]}"
quack
$

Solution 4:[4]

This is a bit old but I answer anyway, you could use temp files. If you do it right you can wrapper it to use them like arrays. For example with this function:

var() { #  set var or add comtent
    case $1 in 
    *=|*=*) 
        local __var_part1=$( echo "$1" | sed -e 's/=.*//' -e 's/[+,-]//' ) # cut +=/=
        local __var_part2=$( echo "$1" | sed -e 's/.*.=//' )
        local __var12=$tmp_dir/$__var_part1
        mkdir -p ${__var12%/*} #create all subdirs if its an array
        case $1 in 
        *+=*)
                # if its an array try to add new item
            if [ -d $tmp_dir/$__var_part1 ] ; then
            printf  -- $__var_part2 > $tmp_dir/$__var_part1/\  $(( 
                $( echo $tmp_dir/$__var_part2/* \
                    | tail  | basename )\ + 1 ))
            else
            printf -- "$__var_part2" >> $tmp_dir/$__var_part1  
            fi
            ;;
        *-=*) false ;;
            # else just add content
            *)  printf  -- "$__var_part2" > $tmp_dir/$__var_part1 ;;
        esac
        ;;  
    *) # just print var
        if [ -d $tmp_dir/$1 ] ; then
        ls $tmp_dir/$1
        elif [ -e $tmp_dir/$1 ] ; then 
        cat $tmp_dir/$1
        else
        return 1
        fi
        ;;
    esac    
}

# you can use mostly like you set vars in bash/shell
var test='Hello Welt!'
# if you need arrays set it like this:
var fruits/0='Apple'
var fruits/1='Banana'

# or if you need a dict:
var contacts/1/name="Max"
var contacts/1/surname="Musterman"

This not the fastest way, but its very flexible, simple and works in nearly all shells.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gilles Quenot
Solution 2
Solution 3 Matthew Groves
Solution 4 Thaodan