Sagar.BlogArticle
All posts
All posts
Bash

Arrays in Bash — Indexed and Associative

Store multiple values in a single variable. Bash supports both indexed arrays (like lists) and associative arrays (like dictionaries). Understand how to create, access, loop, and slice them.

January 9, 20268 min read
BashArraysScripting

Bash arrays let you store a collection of values in one variable. There are two kinds: indexed arrays (zero-based integer indices, like a list) and associative arrays (string keys, like a dict). Both are essential for writing non-trivial scripts.

Indexed Arrays

# Declare and initialize
fruits=("apple" "banana" "cherry" "date")

# Access individual elements (0-based index)
echo "${fruits[0]}"     # apple
echo "${fruits[2]}"     # cherry

# Get all elements
echo "${fruits[@]}"     # apple banana cherry date

# Number of elements
echo "${#fruits[@]}"    # 4

# Get all indices
echo "${!fruits[@]}"    # 0 1 2 3

# Slice: elements 1 and 2 (start index, count)
echo "${fruits[@]:1:2}" # banana cherry
# Add elements
fruits+=("elderberry")        # append
fruits[10]="fig"              # sparse assignment (leaves gaps)

# Modify element
fruits[1]="blueberry"

# Delete element
unset fruits[2]              # removes cherry, leaves gap
echo "${!fruits[@]}"         # 0 1 3 4 10  (gap at index 2)

# Delete entire array
unset fruits

Looping Over Arrays

colors=("red" "green" "blue")

# Loop over values (most common)
for color in "${colors[@]}"; do
    echo "Color: $color"
done

# Loop with index
for i in "${!colors[@]}"; do
    echo "[$i] ${colors[$i]}"
done
# [0] red
# [1] green
# [2] blue

Always use "${array[@]}" with quotes

${array[@]} expands to all elements as separate words, preserving elements that contain spaces. ${array[*]} expands to all elements joined into one string.

Without quotes: ${array[@]} behaves like ${array[*]} — both split on whitespace, corrupting elements with spaces.

Rule: Always write "${array[@]}" (with double quotes) in loops and when passing to commands.

Read Lines into an Array

# mapfile (or readarray) reads lines from stdin into an array
mapfile -t lines < /etc/hosts    # each line becomes an element (-t trims newlines)

echo "Total lines: ${#lines[@]}"

# Or from command output
mapfile -t files < <(find /tmp -name "*.log" -maxdepth 1)

# Process them
for f in "${files[@]}"; do
    echo "Processing: $f"
done

Associative Arrays (Bash 4+)

Associative arrays use string keys instead of integer indices. They must be explicitly declared with declare -A.

declare -A user

user["name"]="Alice"
user["age"]="30"
user["role"]="admin"

# Access
echo "${user["name"]}"        # Alice
echo "${user[role]}"           # admin (quotes around key optional)

# All keys
echo "${!user[@]}"             # name age role (unordered)

# All values
echo "${user[@]}"              # Alice 30 admin (unordered)

# Check if key exists
if [[ -v user["email"] ]]; then
    echo "email is set"
else
    echo "email not set"
fi

# Loop over key-value pairs
for key in "${!user[@]}"; do
    echo "$key = ${user[$key]}"
done

Practical — Counting Words

#!/usr/bin/env bash
# Count word frequency in a file using an associative array

declare -A counts

while read -r word; do
    word="${word,,}"           # lowercase
    word="${word//[^a-z]/}"    # remove non-alpha chars
    [[ -z "$word" ]] && continue
    (( counts["$word"]++ ))
done < <(tr ' ' '
' < "${1:-/dev/stdin}")

# Print sorted by count (desc)
for word in "${!counts[@]}"; do
    echo "${counts[$word]} $word"
done | sort -rn | head -10
Quick Check

What must you do before using an associative array in Bash?

Exercise

Create a script that:

  1. Stores three server names and their IPs in an associative array
  2. Loops over them and prints "Server: web1 → IP: 192.168.1.1"
  3. Checks if a server named "db2" exists and prints an appropriate message