2

For some reason, I am unable to access the array keys with the exclamation point syntax:

declare -a sites
sites=(["fr"]="frederick" ["an"]="annapolis")

for i in "${!sites[@]}"
  do
    echo "key: $i "
  done

This Just echo's out "key : 0"

What am I doing wrong here?

Also, I would like to add the value.

So the our put would be:

key : fr , value : frederick

0

3 Answers 3

0

It Can be done in old bash versions.

In older versions of bash you can use the whole environment variable set to implement associative arrays (also called hashes)

export HASH_PREFIX="I_AM_A_HASH"

hash-set() {
    HASH_NAME="$1" ; shift
    HASH_KEY="$1"  ; shift
    HASH_VAL="$1"  ; shift
    eval "export ${HASH_PREFIX}_${HASH_NAME}_KEY_${HASH_KEY}='$HASH_VAL'"
}
hash-get() {
    HASH_NAME="$1" ; shift
    HASH_KEY="$1"  ; shift
    eval "echo \"\$${HASH_PREFIX}_${HASH_NAME}_KEY_${HASH_KEY}\""
}
hash-keys() {
    HASH_NAME="$1" ; shift
    HASH_PREFIX_NAME_LENGTH=$(( ${#HASH_PREFIX} + ${#HASH_NAME} + 6 ))
    declare -x | while read -r LINE_READ ; do
        LINE_READ="${LINE_READ:11}"
        if [   x"${LINE_READ:0:HASH_PREFIX_NAME_LENGTH}" \
             = x"${HASH_PREFIX}_${HASH_NAME}_KEY_" \
           ]
        then
            LINE_READ="${LINE_READ:HASH_PREFIX_NAME_LENGTH}"
            LINE_READ="${LINE_READ/=*/}"
           echo "${LINE_READ}"
        fi
    done
}

hash-set sites "fr" "frederick"
hash-set sites "an" "annapolis"

for i in $(hash-keys sites) ; do
    echo "key: $i, value: $(hash-get sites $i)"
done

The keys are restricted to the same characters as Environment Variables (0-9,a-z,A-Z,_).

You could workaround by using "_xx" to mean non-alphanumeric ascii values (example "_5f" for "_" and "_3f" for "?"). rosettacode has how to convert back and forth between ascii characters and hex in pure-bash.

Also on mac laptop you can install homebrew then use it to install a newer bash. You could also use the full associative arrays in awk or perl or ruby or python.

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

1 Comment

Please do not use eval, instead see @lhunath's work-around for Bash version that predate 4.
-1

The problem is declare -a.

As per the man page, it should be declare -A.

declare [-aAfFgilrtux] [-p] [name[=value] ...]
    ...
    -a     Each name is an indexed array variable (see Arrays above).
    -A     Each name is an associative array variable (see Arrays above).

Try this instead:

declare -A sites
sites=(["fr"]="frederick" ["an"]="annapolis")

for i in "${!sites[@]}"
  do
    echo "key: $i, value: ${sites[$i]}"
  done

4 Comments

That was what I tried at first. But if I run the script you posted, I get this output: line 3: declare: -A: invalid option declare: usage: declare [-afFirtx] [-p] [name[=value] ...] key: 0, value: annapolis
This might be helpful as well: bash --version GNU bash, version 3.2.48(1)
Associative arrays require bash >= 4.0. See tiswww.case.edu/php/chet/bash/NEWS. And the fact that your man pages for bash 3.2 don't say anything about associative arrays. ;-)
@Mikel, the question was tagged macos, which is always bash 3.2 unless "special measures" (like macports or brew) are taken.
-1

I think it maybe a lack of capitalization getting in the way...

declare -A _sites=( ["fr"]="frederick" ["an"]="annapolis" )

for i in "${!_sites[@]}"; do
    printf '%s -> %s\n' "${i}" "${_sites[$i]}"
done

Resources that where helpful in sorting the above out are not limited to the following;

  • An answer from @anubhava regarding declare -A usage

  • An answer from @lhunath that also covers a work around for Bash versions 3 or lower.

After reading the other posted answer's comments I think you'll want that last listed answer's work-around if stuck with older version of Bash that cannot be updated for reasons.


@Fenn a few more notes...

hash-set() {
    HASH_NAME="$1" ; shift
    HASH_KEY="$1"  ; shift
    HASH_VAL="$1"  ; shift
    eval "export ${HASH_PREFIX}_${HASH_NAME}_KEY_${HASH_KEY}='$HASH_VAL'"
}

... without shifting, or eval, and with required arguments might look like...

hash_set(){
    local _name="${1:?${FUNCNAME[0]} not provided a Hash Name}"
    local _key="${2:?${FUNCNAME[0]} not provided a Hash Key}"
    local _value="${3:?${FUNCNAME[0]} not provided a Value}"
    declare -g "${HASH_PREFIX}_${_name}_KEY_${_key}='${_value}'"
}

... hopefully this is a bit more helpful in translating that answer into something that can be up-voted.

Comments

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.