Declaring Associative Arrays
Unlike indexed arrays, associative arrays require explicit declaration with declare -A (or local -A inside a function) before any keys can be assigned, because Bash needs to know upfront that string keys, not integers, will be used; attempting to assign a string key to a plain array declared without -A silently coerces the key to 0 or fails, a common source of confusion. Once declared, keys are set with array[key]=value, using any string as a key, including ones with spaces if quoted.
Cricket analogy: Declaring declare -A before use is like registering a franchise's player-auction database with named categories (batsman, bowler, all-rounder) before the auction begins — without that upfront setup, the system has no way to file entries by name instead of just a numbered lot.
#!/usr/bin/env bash
set -euo pipefail
declare -A capitals
capitals[France]="Paris"
capitals[Japan]="Tokyo"
capitals["South Korea"]="Seoul" # quoted key with a space
# Bulk declaration
declare -A http_status=(
[200]="OK"
[404]="Not Found"
[500]="Internal Server Error"
)
echo "${capitals[Japan]}" # Tokyo
echo "${http_status[404]}" # Not Found
Iteration, Key Existence, and Unordered-ness
Iterating over an associative array's keys uses "${!array[@]}" (the ! prefix requests the indices/keys rather than the values), and the corresponding values are retrieved inside the loop with ${array[$key]}; critically, Bash does not guarantee any particular iteration order for associative arrays, so scripts that need sorted or deterministic output must explicitly sort the keys first, typically by piping them through sort. Checking whether a key exists (as opposed to whether its value is non-empty) requires the -v test: [[ -v array[key] ]], since a key can legitimately exist with an empty string value.
Cricket analogy: Iterating "${!array[@]}" over a table of team-to-net-run-rate mappings without a guaranteed order is like a scoreboard listing teams in whatever order they were last updated rather than by league standing — you must explicitly sort by points to get the real table.
[[ -v array[key] ]] tests key existence regardless of value, while [[ -n "${array[key]}" ]] tests whether the value is non-empty. These are different questions: a key can be explicitly set to an empty string (array[foo]=""), in which case -v is true but -n is false. Use -v whenever you specifically need to distinguish 'key was never set' from 'key was set to an empty value'.
declare -A word_count
text=(the quick brown fox the lazy fox the dog)
for word in "${text[@]}"; do
word_count["$word"]=$(( ${word_count["$word"]:-0} + 1 ))
done
# Deterministic, sorted output
for word in $(printf '%s\n' "${!word_count[@]}" | sort); do
echo "$word: ${word_count[$word]}"
done
# Key existence vs. non-empty value
word_count[empty_key]=""
[[ -v word_count[empty_key] ]] && echo "empty_key exists"
[[ -n "${word_count[empty_key]}" ]] || echo "but its value is empty"
Practical Patterns: Counters, Lookup Tables, and Sets
Associative arrays are the natural fit for frequency counters (as shown above), configuration lookup tables that map environment names to endpoints or credentials, and set-membership checks where you only care whether a key exists (array[value]=1 for every seen item, then test with -v). Because associative arrays cannot be nested in Bash the way they can in languages like Python or JavaScript, multi-dimensional data is typically flattened into composite keys, e.g. matrix["$row,$col"]=value, which works well but requires careful key-format discipline to avoid collisions.
Cricket analogy: A set-membership pattern like seen_batsmen[Kohli]=1 checking whether a player has already batted this innings mirrors an umpire's mental checklist of who has already had their turn, quickly answerable with a single existence check rather than scanning the whole scorecard.
Associative arrays require Bash 4.0 or later (declare -A is unavailable in Bash 3.2, which macOS shipped as the default /bin/bash for years due to licensing). If your script targets systems that might still have Bash 3.2 as the default (check with bash --version), either require users to install a newer Bash via Homebrew or restructure the logic to avoid associative arrays entirely.
- Associative arrays must be explicitly declared with declare -A (or local -A) before keys can be assigned.
- Keys are arbitrary strings, set with array[key]=value, and can include spaces if the key is quoted.
- Iterate keys with "${!array[@]}" and values with ${array[$key]}; iteration order is not guaranteed and must be sorted explicitly for deterministic output.
- [[ -v array[key] ]] tests whether a key exists, which is different from [[ -n "${array[key]}" ]] testing a non-empty value.
- Associative arrays are ideal for frequency counters, lookup tables, and set-membership checks.
- Bash has no native nested/multi-dimensional associative arrays; flatten with composite keys like array["$row,$col"].
- declare -A requires Bash 4.0+; scripts targeting old default macOS Bash (3.2) need a workaround or a newer Bash install.
Practice what you learned
1. What must be done before you can assign a string key to a Bash array?
2. Why might a script that iterates over "${!config[@]}" produce different output ordering on different runs?
3. Given `arr[foo]=""`, what is the result of `[[ -v arr[foo] ]]` versus `[[ -n "${arr[foo]}" ]]`?
4. How does Bash typically handle a need for a 2D or multi-dimensional lookup table, since it lacks native nested associative arrays?
5. Why might declare -A fail on an older macOS system's default /bin/bash?
Was this page helpful?
You May Also Like
Arrays in Depth
A thorough look at Bash indexed arrays: declaration, expansion pitfalls, slicing, appending, iteration, and the differences between @ and * expansions that trip up even experienced scripters.
Advanced Parameter Expansion
Master Bash's ${...} parameter expansion operators for defaults, substring removal, pattern substitution, case conversion, and length/indirection tricks that eliminate the need for external tools like sed and awk in many scripts.
Bash Script Structure and Shebangs
How to structure a production-quality Bash script, from the shebang line and strict-mode settings to functions, exit codes, and portability considerations.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics