0

I have the same exact problem as described in this SO post ("bash associative array key string with colon is giving error"): https://stackoverflow.com/q/40406187/10639803

The solution is to use declare -A but once I do this, my associative array ceases being global.

Is there a way to declare -A and having it global?

UPDATE: I tried declare -gA as described here: https://stackoverflow.com/a/21151984/10639803 but it doesn't work for me for some reason: As soon as exit the loop that populates my associative array (hashmap), the array gets unset.

Here is the actual bash code (the echo commands inside the loop are to verify that values are indeed extracted and assigned):

declare -gA HOSTS_START_MAP
find "$TEMP" -type f -name "debug.log*" -exec grep -F "STARTING HOST " {} \; |
while IFS= read -r HOST_START_LINE; do
    if [[ $HOST_START_LINE =~ (.*)(DEBUG)(.*)(STARTING HOST)([ 0-9]*)(.*)(CALCULATION) ]]
    then
        HOST_START_TIME=$(echo "${BASH_REMATCH[1]}" | xargs)
        HOST_NAME=$(echo "${BASH_REMATCH[6]}" | xargs)
        # echo ">$HOST_NAME< ... >$HOST_START_TIME<"
        HOSTS_START_MAP[$HOST_NAME]=$HOST_START_TIME
        # echo $HOST_NAME --- ${HOSTS_START_MAP[$HOST_NAME]}
    fi
done

echo ${#HOSTS_START_MAP[@]}
for MYKEY in "${!HOSTS_START_MAP[@]}"; do echo $MYKEY --- ${HOSTS_START_MAP[@]}; done
datsb
  • 303
  • 1
  • 2
  • 10

1 Answers1

1

The problem you're seeing has nothing to do with the declaration of the variable. It's because you are setting the variable in a subshell and expecting its value(s) to be available in the parent.

You can see this effect here, using the same structure as your code but a much simpler example:

a=""
echo find | while read item; do a="$item"; done
echo "a=$a"

Fortunately, when using bash there is a quick solution available by rewriting the code in a different shape:

a=""
while read item; do a="$item"; done < <( echo find )
echo "a=$a"

In real code, use while IFS= read -r item to ensure that item receives unprocessed input. I've omitted the protections for what I hope is clarity in the examples.


In the comments on this answer you've asked for a revised version of your code. I can't test it, which is why I hesitated putting it here in the first place, but it should look like this:

#!/bin/bash
#
declare -gA HOSTS_START_MAP
while IFS= read -r HOST_START_LINE; do
    if [[ $HOST_START_LINE =~ (.*)(DEBUG)(.*)(STARTING HOST)([ 0-9]*)(.*)(CALCULATION) ]]
    then
        HOST_START_TIME=$(echo "${BASH_REMATCH[1]}" | xargs)
        HOST_NAME=$(echo "${BASH_REMATCH[6]}" | xargs)
        # echo ">$HOST_NAME< ... >$HOST_START_TIME<"
        HOSTS_START_MAP[$HOST_NAME]=$HOST_START_TIME
        # echo $HOST_NAME --- ${HOSTS_START_MAP[$HOST_NAME]}
    fi
done < <( find "$TEMP" -type f -name "debug.log*" -exec grep -F "STARTING HOST " {} \; )

echo ${#HOSTS_START_MAP[@]}
for MYKEY in "${!HOSTS_START_MAP[@]}"; do echo $MYKEY --- ${HOSTS_START_MAP[@]}; done
Chris Davies
  • 116,213
  • 16
  • 160
  • 287