Search code examples
arraysbashvariablesscope

Bash variable scope issue in arrays


This one has been bugging me the whole day today, and I swear it used to work. My variables are not available everywhere.

I had automated a quite complex procedure but now we switched to a new architecture and I am removing all the unnecessary code from the script. But now my variables don't seem to be accessible globally anymore.

I have a main script that calls different functions in cases such as ./script.sh install, the usual stuff.

At some point, I source some defaults via source ./defaults.sh. One of the variables defined there is: NETWORK_NAME=my_net

There is a another file that contains an array of options including the configuration for each version, that I call via source ./version_number.sh, for example:

declare -A VAULT_SETTINGS
VAULT_SETTINGS[SECRETS_ENGINE_PATH]="secret"

The problem is now when I run my code, for example:

func_vault_create_container () {
        docker create --name "${VAULT_SETTINGS[SERVICE_NAME]}" \
--hostname "${VAULT_SETTINGS[HOST]}" --network "${NETWORK_NAME}" \
-v "${VAULT_SETTINGS[DATA_VOLUME]}":/vault \
-v "${VAULT_SETTINGS[INIT_VOLUME]}":/usr/init --user "${VAULT_SETTINGS[USER]}" -e SKIP_SETCAP=1 -e VAULT_DISABLE_MLOCK=1 -e SET_NAME="${VAULT_SETTINGS[HOST]}" \
-e SECRETS_ENGINE_PATH="${VAULT_SETTINGS[SECRETS_ENGINE_PATH]}" -e POLICY_NAME="${VAULT_SETTINGS[POLICY_NAME]}" \
-e ROLE_NAME="${VAULT_SETTINGS[ROLE_NAME]}" \
-e POLICY_FILE="${VAULT_SETTINGS[POLICY_FILE]}" \
-e APP_ROLE="${VAULT_SETTINGS[APP_ROLE]}" -e VAULT_ADDR="https://127.0.0.1:8200" "${VAULT_SETTINGS[IMAGE]}" \
bash /entrypoint.sh > /dev/null
}

The network name is there, but nothing else. Here is a sample of the output of bash -x:

...
...
+ source ./helpers/defaults.sh
++ CHECK_ONLINE_IPS=(1.1.1.1 9.9.9.9 8.8.8.8)
++ PAUSEMSG='Press any key to continue or Ctrl-C to quit.'
++ ERRORFILE=./error.txt
++ NETWORK_NAME=my_net
...
...
++ source ./config/2023.3.1-3434-docker.sh
+++ declare -A VAULT_SETTINGS
+++ VAULT_SETTINGS[VAULT_VERSION]=2023.3.1-3434-docker
+++ VAULT_SETTINGS[USER]=vault
+++ VAULT_SETTINGS[PORT]=8200
+++ VAULT_SETTINGS[CONTAINER_ID]=0
+++ VAULT_SETTINGS[SECRETS_ENGINE_PATH]=secret
...
...
...
+ func_vault_create_container
+ docker create --name '' --hostname '' --network my_net -v :/vault -v :/usr/init --user '' -e SKIP_SETCAP=1 -e VAULT_DISABLE_MLOCK=1 -e SET_NAME= -e SECRETS_ENGINE_PATH= -e POLICY_NAME= -e ROLE_NAME= -e POLICY_FILE= -e APP_ROLE= -e VAULT_ADDR=https://127.0.0.1:8200 '' bash /entrypoint.sh
invalid reference format

As you can see, everything I have declared in the array is missing, even though it has been loaded. But $NETWORK_NAME loads fine! Any clues on where I could start my investigation from? This used to work before but I can't find what I have different there.

Thanks, as always.

PS: I've just tried source ./config/2023.3.1-3434-docker.sh from within func_vault_create_container, but this is not a fix for me because sometimes the values in the array will change and I can't trust the reference values set in the ./config/2023.3.1-3434-docker.sh will be valid.


Solution

  • It seems based on your trace output that the vault setting gets sourced inside a function/subshell.

    As described from declare --help, when declare is used in a function, it is implicitly local and not available outside the function.

    declare: declare [-aAfFgiIlnrtux] [-p] [name[=value] ...]
        Set variable values and attributes.
    
        ...
        
        Options:
          ...
    
          -g        create global variables when used in a shell function; otherwise
                    ignored
          ...
    
    
        When used in a function, `declare' makes NAMEs local, as with the `local'
        command.  The `-g' option suppresses this behavior.
    
    

    The solution to the problem is to use -g to make it global.

    # -A Asociative array
    # -g Global
    declare -Ag VAULT_SETTINGS