18

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.

Community
  • 1
  • 1
Adam S
  • 8,945
  • 17
  • 67
  • 103
  • If your use can accept wrapping it in GNU Parallel, you can use `env_parallel`: https://www.gnu.org/software/parallel/env_parallel.html – Ole Tange Mar 24 '19 at 20:46

4 Answers4

7

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

Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
  • 4
    Going a little deeper, the environment is defined by the operating system; shells just provide a way to populate the environment. POSIX (to use as an example) does not provide a definition for structured data for its environment variables; each value is simply a string. Any attempt by `bash` to convert an array (regular or associative) to a single string would be specific to `bash`. That creates a portability nightmare, as the environment is no longer simply defined by the operating system, but by whatever method any user might decide to use to launch a program. – chepner Oct 18 '12 at 12:55
6

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[@]}" 
LookAheadAtYourTypes
  • 1,629
  • 2
  • 20
  • 35
  • 1
    Instead of manually creating and deleting the file you could use process substitution: `DEF_MAP=<(declare -p map) bash -c 'source "$DEF_MAP"; # do something with map'`. Here, `bash -c` is just a placeholder for the command to which you want to export your array. – Socowi Jun 15 '20 at 11:40
0

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.

Thaodan
  • 107
  • 1
  • 3
  • 10
-1

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
$
Matthew Groves
  • 25,181
  • 9
  • 71
  • 121
NejatHakan
  • 15
  • 1
  • Wow, not even a single upvote for this? This one worked for me in `zsh` without all the hassle of creating a temp file etc. – Farzad Oct 04 '22 at 12:36
  • doesnt declare -x already export it – Stan Nov 09 '22 at 23:18
  • 2
    `${animals[duck]}` is expanded in the parent shell because of the double quoting. If this worked, it should still work with single quotes, or with `declare -p animals` instead of `echo ${animals[duck]}`, but it doesn't, so this answer isn't correct for Bash. – Benjamin W. Nov 15 '22 at 17:48