Shell Scripting Bash Associative Arrays Basics May 2026

Shell Scripting Associative Arrays

Master bash associative arrays — key-value stores that bring dictionary-like power to shell scripts. Learn declaration, CRUD operations, iteration, real-world lookup tables, and counters.

Associative arrays (also called hash maps or dictionaries) let you map arbitrary string keys to values. Introduced in bash 4.0, they eliminate the need for complex awk or grep workarounds when you need key-value lookups inside a script.

⚠ Bash 4.0+ required — Associative arrays need bash 4.0 or later. macOS ships with bash 3.2 by default. Check with bash --version. On macOS install bash via Homebrew: brew install bash. On Linux, bash 4+ is standard since ~2009.

You must use declare -A to create an associative array. Without it, bash treats it as an indexed array and silently discards your string keys.

BASH
# ── Declare (mandatory for associative arrays) ────────────
declare -A colors
declare -A db_config

# ── Populate after declaration ────────────────────────────
colors["red"]="#ff0000"
colors["green"]="#00ff00"
colors["blue"]="#0000ff"
colors["orange"]="#e0701a"

# ── Declare and populate in one statement ─────────────────
declare -A db_config=(
  ["host"]="prod-db-01"
  ["port"]="3306"
  ["name"]="appdb"
  ["user"]="app_user"
)

# ── Access a value by key ─────────────────────────────────
echo "Red   : ${colors[red]}"
echo "Host  : ${db_config[host]}"
echo "Port  : ${db_config[port]}"

# Keys with spaces — quote the key
declare -A server_info
server_info["app server"]="10.0.1.10"
server_info["db server"]="10.0.1.20"
echo "App: ${server_info["app server"]}"
bash — associative array basics
vriddh@prod-01:~/scripts$bash assoc_demo.sh
Red : #ff0000
Host : prod-db-01
Port : 3306
App: 10.0.1.10
BASH
declare -A ports=(
  ["ssh"]=22
  ["http"]=80
  ["https"]=443
  ["mysql"]=3306
  ["postgres"]=5432
)

# ── All keys ──────────────────────────────────────────────
echo "Keys : ${!ports[@]}"

# ── All values ────────────────────────────────────────────
echo "Values: ${ports[@]}"

# ── Count of entries ──────────────────────────────────────
echo "Count : ${#ports[@]}"

# ── Iterate over keys ─────────────────────────────────────
echo "All services:"
for svc in "${!ports[@]}"; do
  echo "  ${svc} → ${ports[$svc]}"
done

# ── Iterate over values only ──────────────────────────────
for port in "${ports[@]}"; do
  echo "  port: ${port}"
done

# ── Sorted output (keys are unordered in bash) ────────────
for svc in $(echo "${!ports[@]}" | tr ' ' '\n' | sort); do
  printf "  %-12s %s\n" "${svc}" "${ports[$svc]}"
done
bash — ports lookup
vriddh@prod-01:~/scripts$bash ports.sh
Keys : mysql postgres ssh https http
Values: 3306 5432 22 443 80
Count : 5
All services (sorted):
http 80
https 443
mysql 3306
postgres 5432
ssh 22
💡 Keys are unordered — Associative arrays do not preserve insertion order. If you need sorted output, pipe the keys through sort as shown above. Never rely on the order of ${!map[@]}.
BASH
declare -A config=(
  ["env"]="staging"
  ["debug"]="false"
  ["workers"]="4"
)

# ── UPDATE — simply reassign ──────────────────────────────
config["env"]="production"
config["workers"]="16"

# ── ADD new key ───────────────────────────────────────────
config["timeout"]="30"
config["log_level"]="INFO"

# ── CHECK if key exists ───────────────────────────────────
if [[ -v config["env"] ]]; then
  echo "env key exists: ${config[env]}"
fi

# Alternative check (works in bash 3 too)
if [[ -n "${config[env]+set}" ]]; then
  echo "env is set"
fi

# ── DELETE a key ──────────────────────────────────────────
unset 'config[debug]'         # Quote to prevent glob expansion

# ── DELETE entire array ───────────────────────────────────
unset config

# ── Safe access with default ──────────────────────────────
declare -A settings
timeout="${settings[timeout]:-60}"   # Default 60 if key missing
echo "Timeout: ${timeout}"

One of the most powerful uses of associative arrays is counting occurrences — something that would otherwise require a temporary file or complex pipeline.

BASH
#!/usr/bin/env bash
# count_http_codes.sh — Count HTTP status codes in access log

declare -A code_count

while read -r line; do
  # Extract HTTP status code (field 9 in Apache/Nginx combined log)
  code=$(echo "${line}" | awk '{print $9}')
  [[ "${code}" =~ ^[0-9]+$ ]] || continue
  (( code_count["${code}"]++ ))
done < /var/log/nginx/access.log

echo "HTTP Status Code Summary"
echo "─────────────────────────"
for code in $(echo "${!code_count[@]}" | tr ' ' '\n' | sort); do
  printf "  HTTP %-6s %d requests\n" "${code}" "${code_count[$code]}"
done
Key 2xx Success 3xx/4xx 5xx Errors
bash — count_http_codes.sh
vriddh@prod-01:~/scripts$./count_http_codes.sh
HTTP Status Code Summary
─────────────────────────
HTTP 200 14823 requests
HTTP 201 342 requests
HTTP 301 89 requests
HTTP 404 213 requests
HTTP 500 17 requests
HTTP 502 4 requests
BASH
#!/usr/bin/env bash
# deploy.sh — Environment-aware deployment using lookup table

declare -A DB_HOSTS=(
  ["dev"]="dev-db-01.internal"
  ["staging"]="stg-db-01.internal"
  ["production"]="prod-db-01.internal"
)

declare -A APP_PORTS=(
  ["dev"]="8080"
  ["staging"]="8081"
  ["production"]="80"
)

declare -A REPLICAS=(
  ["dev"]="1"
  ["staging"]="2"
  ["production"]="8"
)

ENV="${1:-dev}"

# Validate environment
if [[ ! -v DB_HOSTS["${ENV}"] ]]; then
  echo "ERROR: Unknown environment '${ENV}'" >&2
  echo "Valid: ${!DB_HOSTS[*]}" >&2
  exit 1
fi

echo "Deploying to: ${ENV}"
echo "  DB Host  : ${DB_HOSTS[$ENV]}"
echo "  Port     : ${APP_PORTS[$ENV]}"
echo "  Replicas : ${REPLICAS[$ENV]}"
bash — deploy.sh
vriddh@prod-01:~/scripts$./deploy.sh production
Deploying to: production
DB Host : prod-db-01.internal
Port : 80
Replicas : 8
vriddh@prod-01:~/scripts$./deploy.sh uat
ERROR: Unknown environment 'uat'
Valid: dev staging production

Bash cannot pass associative arrays directly to functions. The standard pattern is to pass the array name as a string and use a nameref (declare -n) inside the function — available from bash 4.3+.

BASH
#!/usr/bin/env bash
# Passing associative arrays via nameref (bash 4.3+)

print_map() {
  local -n map="${1}"       # -n creates a nameref
  local title="${2:-Map}"
  echo "── ${title} ──"
  for key in $(echo "${!map[@]}" | tr ' ' '\n' | sort); do
    printf "  %-15s = %s\n" "${key}" "${map[$key]}"
  done
}

merge_maps() {
  local -n source="${1}"
  local -n target="${2}"
  for key in "${!source[@]}"; do
    target["${key}"]="${source[$key]}"
  done
}

declare -A defaults=(["timeout"]="30" ["retries"]="3" ["log"]="INFO")
declare -A overrides=(["timeout"]="60" ["debug"]="true")

merge_maps overrides defaults
print_map defaults "Final Config"
bash — nameref demo
vriddh@prod-01:~/scripts$bash nameref_demo.sh
── Final Config ──
debug = true
log = INFO
retries = 3
timeout = 60
✔ When to use associative arrays — Use them any time you find yourself doing grep lookups in a loop, maintaining parallel arrays with matching indexes, or using temp files to store key-value pairs. An associative array is cleaner, faster, and entirely in-memory.