6

According to man bash it is possible to assign an associative array with a flat list of key-value pairs:

[…] or a list of words that is interpreted as a sequence of alternating keys and values: name=( key1 value1 key2 value2 ...)

This does work, but it does not work to use an array to get these "alternating keys and values": name=( "${someArray[@]}" )

Consider this example:

#!/usr/bin/env bash

set -u

#declare -A map=(one ONE two TWO)
array=(one ONE two TWO)
declare -A map=("${array[@]}")
# Workaround with `eval`:
#eval "declare -A map=(${array[@]@Q})"

echo "KEYS:"
printf '    %s\n' "${!map[@]}"
echo "VALUES:"
printf '    %s\n' "${map[@]}"

echo "${#map[@]}"

Actual output:

KEYS:
    one ONE two TWO
VALUES:
    

Expected output:

KEYS:
    two
    one
VALUES:
    TWO
    ONE

Why is that so?

7
  • 1
    Although probably clear, declare -A map=(${array[@]}) does not work either. Commented Jan 11, 2024 at 13:14
  • Any reference for this behaviour, @mandy8055? It also happens if assigned later, i.e. after declared. Commented Jan 11, 2024 at 13:17
  • No, it doesn't, @Amessihel. I know how to decalre an associative array. This is about using another array for doing this. Commented Jan 11, 2024 at 13:18
  • 1
    Furthermore, your claim seems to be wrong, @mandy8055: There is only a single key one ONE two TWO. Commented Jan 11, 2024 at 13:20
  • @doak, what's your bash version? Commented Jan 11, 2024 at 13:27

1 Answer 1

6

Include the array parens in the quoted string to preserve the whitespace between items for the declaration. Also, if keys or values themselves contain whitespace, use @Q to quote each item:

array=(one 'O NE' 'tw o' TWO)
declare -A map="(${array[@]@Q})"

echo "KEYS:"
printf '    %s\n' "${!map[@]}"
echo "VALUES:"
printf '    %s\n' "${map[@]}"

echo "${#map[@]}"
KEYS:
    one
    tw o
VALUES:
    O NE
    TWO
2

Tested with GNU bash 5.2.21

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

3 Comments

Indeed, using declare -A map="(${array[@]@Q})" works. Any explanation for this? It looks like Bash is using eval internally for this, doesn't it?
I'd guess that ${array[@]} is evaluated later than declare -A map=… is parsed. Therefore, declare -A map=(one ONE two TWO) is recognozed as intended whereas declare -A map=(${array[@]}) is prematurely broken down into multiple arguments. This can be mitigated by using quotes as in declare -A map="(${array[@]})" or even declare -A "map=(${array[@]})". The important thing is to provide two arguments to declare: -A and the "content".
U could use declare -p map array for showing content of map and array variable. Or even this: paste -d '' <(printf ' - %-10s\n' "${!map[@]}") <(printf ' \47%s\47\n' "${map[@]}")

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.