Bash Arrays and Associative Arrays
September 18, 2026
Bash arrays are one of those features that many people overlook in favor of piping commands together. Once you start using them, you will find yourself reaching for them constantly. Let's walk through both indexed arrays and associative arrays with practical examples.
Indexed Arrays
An indexed array is a numbered list of values. You can create one in several ways.
# Method 1: declare and assign
fruits=("apple" "banana" "cherry" "date")
# Method 2: assign by index
colors[0]="red"
colors[1]="green"
colors[2]="blue"
# Method 3: from command output
log_files=($(find /var/log -name "*.log" -maxdepth 1))
Accessing Elements
Array indexing starts at zero. Use ${array[index]} to read a value.
fruits=("apple" "banana" "cherry" "date")
echo "${fruits[0]}" # apple
echo "${fruits[2]}" # cherry
echo "${fruits[-1]}" # date (last element)
All Elements and Length
Two special expansions give you every element and the total count.
echo "${fruits[@]}" # apple banana cherry date
echo "${#fruits[@]}" # 4
The [@] expansion is important. Always use it inside double quotes when iterating, so elements with spaces are preserved.
files=("my report.txt" "data file.csv" "notes.md")
# Wrong - splits on spaces
for f in ${files[@]}; do echo "$f"; done
# Correct - preserves spaces
for f in "${files[@]}"; do echo "$f"; done
Adding and Removing Elements
# Append to the end
fruits+=("elderberry")
fruits+=("fig" "grape")
# Remove by index (leaves a gap)
unset 'fruits[1]'
# Show indices to see the gap
echo "${!fruits[@]}" # 0 2 3 4 5 6
# Remove the entire array
unset fruits
Note that unset on an index does not re-number the remaining elements. If you need a contiguous array after deletion, reassign it.
fruits=("${fruits[@]}")
Slicing
You can extract a subset of an array with ${array[@]:offset:length}.
letters=("a" "b" "c" "d" "e" "f")
echo "${letters[@]:2:3}" # c d e
echo "${letters[@]:4}" # e f (from index 4 to the end)
Practical Example: Batch Processing Files
Here is a script that collects files into an array, processes them in batches, and reports progress.
#!/bin/bash
batch_size=5
files=($(find /var/log -name "*.log" -maxdepth 1 2>/dev/null))
total=${#files[@]}
echo "Found $total log files"
for ((i = 0; i < total; i += batch_size)); do
batch=("${files[@]:i:batch_size}")
echo "Processing batch: ${batch[*]}"
# do something with the batch
du -sh "${batch[@]}" 2>/dev/null
echo "---"
done
Associative Arrays
Associative arrays use string keys instead of numeric indices. They require an explicit declare -A and are available in bash 4.0 and later.
declare -A server_ips
server_ips[web]="203.0.113.10"
server_ips[db]="203.0.113.20"
server_ips[cache]="203.0.113.30"
You can also initialize them inline.
declare -A ports=(
[http]=80
[https]=443
[ssh]=22
[mysql]=3306
)
Accessing Values
echo "${ports[ssh]}" # 22
echo "${ports[https]}" # 443
Keys and Iteration
# All keys
echo "${!ports[@]}" # http https ssh mysql
# All values
echo "${ports[@]}" # 80 443 22 3306
# Iterate over key-value pairs
for key in "${!ports[@]}"; do
echo "$key -> ${ports[$key]}"
done
Checking If a Key Exists
if [[ -v ports[ssh] ]]; then
echo "SSH port is ${ports[ssh]}"
fi
if [[ ! -v ports[ftp] ]]; then
echo "FTP port not defined"
fi
Practical Example: Service Health Check
This script uses an associative array to map service names to their expected ports and checks whether each is listening.
#!/bin/bash
declare -A services=(
[sshd]=22
[nginx]=443
[mysql]=3306
[redis]=6379
)
for service in "${!services[@]}"; do
port=${services[$service]}
if ss -tlnp | grep -q ":${port} "; then
echo "[OK] $service is listening on port $port"
else
echo "[FAIL] $service is NOT listening on port $port"
fi
done
Practical Example: Config File Parser
Associative arrays are a natural fit for reading key-value config files.
#!/bin/bash
declare -A config
while IFS='=' read -r key value; do
# skip comments and blank lines
[[ "$key" =~ ^#.*$ || -z "$key" ]] && continue
# trim whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
config[$key]="$value"
done < /etc/myapp.conf
echo "Database host: ${config[db_host]}"
echo "Database port: ${config[db_port]:-3306}"
The ${config[db_port]:-3306} syntax provides a default value when the key is missing, which we cover more in the parameter expansion article.
Things to Watch Out For
Quoting matters. Always use "${array[@]}" with double quotes when iterating. Without quotes, elements containing spaces will be split into separate words.
Sparse arrays. After using unset on an index, the array has gaps. Iteration with for item in "${array[@]}" still works, but direct index access may not hit what you expect.
Bash version. Associative arrays require bash 4.0 or later. Check with bash --version. Most modern Linux distributions ship bash 5.x, but if you are writing portable scripts, keep this in mind.
No nested arrays. Bash does not support arrays of arrays. If you need that, you are probably better off reaching for Python or another language.
Arrays are a powerful tool for organizing data in your shell scripts. Indexed arrays work well for ordered lists, while associative arrays are ideal for key-value lookups and configuration data.