Lesson 11 / 25
Indexed and Associative Arrays
Store lists and key-value pairs in Bash arrays.
Lists without word-splitting bugs
Arrays hold lists safely, including elements with spaces. Indexed arrays: files=("a b.txt" c.txt), append with files+=(d.txt), read with "${files[0]}", all elements with "${files[@]}" (each element one word, the array equivalent of "$@"), count with ${#files[@]} and indices with ${!files[@]}. Associative arrays (Bash 4+, not available in macOS's built-in Bash 3.2) map keys to values: declare -A ports=([web]=8080 [api]=9000), read ${ports[api]}, iterate keys with "${!ports[@]}". mapfile -t lines < file (also called readarray) reads a file into an array, one line per element. Arrays are the right way to build command lines dynamically: collect options in an array and run "${cmd[@]}", instead of building a string and relying on word splitting. Bash arrays are one-dimensional; if you need nested data structures, it is time for another language or for jq with JSON.
Arrays for safe command building
Each option stays a separate argument, even with spaces.
declare -A ports=([web]=8080 [api]=9000 [metrics]=9100)
for svc in "${!ports[@]}"; do
printf '%-8s -> %s\n' "$svc" "${ports[$svc]}"
done
mapfile -t hosts < hosts.txt
echo "loaded ${#hosts[@]} hosts; first: ${hosts[0]}"
rsync_args=(-az --delete --exclude "*.tmp")
if [[ ${DRY_RUN:-0} == 1 ]]; then
rsync_args+=(--dry-run)
fi
rsync "${rsync_args[@]}" "./site/" "deploy@web1:/var/www/my site/"A tray of labelled jars
A string with spaces is loose spices in one bag: you cannot tell where one ends and the next begins. An array is a tray of separate jars, each holding exactly one item, however oddly shaped.
Quick check: How do you expand every element of an array as separate, correctly quoted words?
- $arr
- "${arr[@]}"
- ${arr}
- ${arr[*]}
Answer
"${arr[@]}" — "${arr[@]}" expands each element as its own word, preserving spaces inside elements.