| name | advanced-array-patterns |
| description | Advanced bash array patterns including mapfile, readarray, associative arrays, and array manipulation (2025) |
CRITICAL GUIDELINES
Windows File Path Requirements
MANDATORY: Always Use Backslashes on Windows for File Paths
When using Edit or Write tools on Windows, you MUST use backslashes (\) in file paths, NOT forward slashes (/).
Advanced Bash Array Patterns (2025)
Overview
Comprehensive guide to bash arrays including indexed arrays, associative arrays, mapfile/readarray, and advanced manipulation patterns following 2025 best practices.
Indexed Arrays
Declaration and Initialization
#!/usr/bin/env bash
set -euo pipefail
files=("file1.txt" "file2.txt" "file with spaces.txt")
declare -a numbers=(1 2 3 4 5)
fruits[0]="apple"
fruits[1]="banana"
fruits[2]="cherry"
files_bad=$(ls)
mapfile -t files_good < <(find . -name "*.txt")
numbers=({1..100})
letters=({a..z})
Array Operations
#!/usr/bin/env bash
set -euo pipefail
arr=("first" "second" "third" "fourth" "fifth")
echo "Length: ${#arr[@]}"
echo "First: ${arr[0]}"
echo "Last: ${arr[-1]}"
echo "Second to last: ${arr[-2]}"
for item in "${arr[@]}"; do
echo "Item: $item"
done
for idx in "${!arr[@]}"; do
echo "Index $idx: ${arr[$idx]}"
done
echo "${arr[@]:1:3}"
echo "${arr[@]:2}"
arr+=("sixth")
arr=("${arr[@]:0:2}" "inserted" "${arr[@]:2}")
unset 'arr[2]'
arr_new=()
for item in "${arr[@]}"; do
[[ "$item" != "second" ]] && arr_new+=("$item")
done
arr=("${arr_new[@]}")
if [[ ${#arr[@]} -eq 0 ]]; then
echo "Array is empty"
fi
contains() {
local needle="$1"
shift
local item
for item in "$@"; do
[[ "$item" == "$needle" ]] && return 0
done
return 1
}
if contains "third" "${arr[@]}"; then
echo "Found 'third'"
fi
Array Transformation
#!/usr/bin/env bash
set -euo pipefail
arr=("apple" "banana" "cherry" "date")
upper_arr=()
for item in "${arr[@]}"; do
upper_arr+=("${item^^}")
done
filtered=()
for item in "${arr[@]}"; do
[[ ${#item} -gt 5 ]] && filtered+=("$item")
done
IFS=','
joined="${arr[*]}"
unset IFS
echo "$joined"
IFS=',' read -ra split_arr <<< "one,two,three"
declare -A seen
unique=()
for item in "${arr[@]}"; do
if [[ -z "${seen[$item]:-}" ]]; then
seen[$item]=1
unique+=("$item")
fi
done
readarray -t sorted < <(printf '%s\n' "${arr[@]}" | sort)
reversed=()
for ((i=${#arr[@]}-1; i>=0; i--)); do
reversed+=("${arr[$i]}")
done
readarray -t reversed < <(printf '%s\n' "${arr[@]}" | tac)
Associative Arrays (Bash 4+)
Declaration and Usage
#!/usr/bin/env bash
set -euo pipefail
declare -A config
config["host"]="localhost"
config["port"]="8080"
config["debug"]="true"
declare -A user=(
[name]="John Doe"
[email]="john@example.com"
[role]="admin"
)
echo "Host: ${config[host]}"
echo "User: ${user[name]}"
echo "${config[missing]:-default}"
if [[ -v config[host] ]]; then
echo "Host is set"
fi
if [[ -n "${config[host]+x}" ]]; then
echo "Host key exists (even if empty)"
fi
echo "Keys: ${!config[@]}"
echo "Values: ${config[@]}"
echo "Size: ${#config[@]}"
for key in "${!config[@]}"; do
echo "$key = ${config[$key]}"
done
unset 'config[debug]'
config=()
Real-World Associative Array Patterns
#!/usr/bin/env bash
set -euo pipefail
parse_config() {
local config_file="$1"
declare -gA CONFIG
while IFS='=' read -r key value; do
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "$key" ]] && continue
key="${key//[[:space:]]/}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
CONFIG["$key"]="$value"
done < "$config_file"
}
parse_config "/etc/myapp.conf"
echo "Database: ${CONFIG[database]:-not set}"
count_words() {
local file="$1"
declare -A word_count
while read -ra words; do
for word in "${words[@]}"; do
word="${word,,}"
word="${word//[^a-z]/}"
[[ -n "$word" ]] && ((word_count[$word]++))
done
done < "$file"
for word in "${!word_count[@]}"; do
echo "${word_count[$word]} $word"
done | sort -rn | head -10
}
declare -A CACHE
cached_expensive_operation() {
local key="$1"
if [[ -n "${CACHE[$key]+x}" ]]; then
echo "${CACHE[$key]}"
return 0
fi
local result
result=$(expensive_computation "$key")
CACHE["$key"]="$result"
echo "$result"
}
declare -A data
data["user.name"]="John"
data["user.email"]="john@example.com"
data["user.address.city"]="NYC"
data["user.address.zip"]="10001"
echo "City: ${data[user.address.city]}"
mapfile / readarray (Bash 4+)
Basic Usage
#!/usr/bin/env bash
set -euo pipefail
mapfile -t lines < file.txt
readarray -t lines < file.txt
for line in "${lines[@]}"; do
echo "Line: $line"
done
mapfile -t files < <(find . -name "*.sh")
mapfile -t data <<'EOF'
line1
line2
line3
EOF
Advanced mapfile Options
#!/usr/bin/env bash
set -euo pipefail
mapfile -t -n 10 first_10 < large_file.txt
mapfile -t -s 1 skip_header < data.csv
existing_array=("a" "b")
mapfile -t -O "${#existing_array[@]}" existing_array < more_data.txt
mapfile -t -d '' files < <(find . -name "*.txt" -print0)
process_chunk() {
local index=$1
echo "Processing lines around index $index" >&2
}
export -f process_chunk
mapfile -t -c 1000 -C process_chunk lines < huge_file.txt
CSV Processing with mapfile
#!/usr/bin/env bash
set -euo pipefail
parse_csv() {
local csv_file="$1"
local -n result_array="$2"
while IFS=',' read -ra row; do
result_array+=("${row[*]}")
done < "$csv_file"
}
declare -A csv_data
row_num=0
while IFS=',' read -ra fields; do
for col_num in "${!fields[@]}"; do
csv_data["$row_num,$col_num"]="${fields[$col_num]}"
done
((row_num++))
done < data.csv
echo "Row 2, Col 3: ${csv_data[2,3]}"
Performance Patterns
Efficient Array Building
#!/usr/bin/env bash
set -euo pipefail
slow_build() {
local arr=()
for i in {1..1000}; do
arr+=("$(echo "$i")")
done
}
fast_build() {
local arr=()
for i in {1..1000}; do
arr+=("$i")
done
}
fastest_file_read() {
mapfile -t arr < file.txt
}
Avoid Subshells in Loops
#!/usr/bin/env bash
set -euo pipefail
slow_process() {
local sum=0
for num in "${numbers[@]}"; do
result=$(echo "$num * 2" | bc)
((sum += result))
done
}
fast_process() {
local sum=0
for num in "${numbers[@]}"; do
((sum += num * 2))
done
}
while read -r line1 <&3 && read -r line2 <&4; do
echo "$line1 | $line2"
done 3< <(command1) 4< <(command2)
Large Array Operations
#!/usr/bin/env bash
set -euo pipefail
process_in_chunks() {
local -n arr="$1"
local chunk_size="${2:-1000}"
local len="${#arr[@]}"
for ((i=0; i<len; i+=chunk_size)); do
local chunk=("${arr[@]:i:chunk_size}")
process_chunk "${chunk[@]}"
done
}
while IFS= read -r line; do
process_line "$line"
done < huge_file.txt
Bash 5.3+ Array Features
Enhanced Array Subscripts
#!/usr/bin/env bash
set -euo pipefail
declare -A config
key="host"
config[$key]="localhost"
declare -A special
special[@]="at sign value"
special[*]="asterisk value"
special[normal]="normal value"
unset 'special[@]'
GLOBSORT with Arrays
#!/usr/bin/env bash
set -euo pipefail
GLOBSORT="-mtime"
recent_files=(*.txt)
GLOBSORT="size"
files_by_size=(*.log)
GLOBSORT="name"
Common Array Patterns
Stack Implementation
#!/usr/bin/env bash
set -euo pipefail
declare -a STACK=()
push() {
STACK+=("$1")
}
pop() {
if [[ ${#STACK[@]} -eq 0 ]]; then
echo "Stack empty" >&2
return 1
fi
echo "${STACK[-1]}"
unset 'STACK[-1]'
}
peek() {
if [[ ${#STACK[@]} -gt 0 ]]; then
echo "${STACK[-1]}"
fi
}
push "first"
push "second"
push "third"
echo "Top: $(peek)"
echo "Pop: $(pop)"
echo "Pop: $(pop)"
Queue Implementation
#!/usr/bin/env bash
set -euo pipefail
declare -a QUEUE=()
enqueue() {
QUEUE+=("$1")
}
dequeue() {
if [[ ${#QUEUE[@]} -eq 0 ]]; then
echo "Queue empty" >&2
return 1
fi
echo "${QUEUE[0]}"
QUEUE=("${QUEUE[@]:1}")
}
enqueue "task1"
enqueue "task2"
enqueue "task3"
echo "Next: $(dequeue)"
echo "Next: $(dequeue)"
Set Operations
#!/usr/bin/env bash
set -euo pipefail
array_union() {
local -n arr1="$1"
local -n arr2="$2"
local -A seen
local result=()
for item in "${arr1[@]}" "${arr2[@]}"; do
if [[ -z "${seen[$item]:-}" ]]; then
seen[$item]=1
result+=("$item")
fi
done
printf '%s\n' "${result[@]}"
}
array_intersection() {
local -n arr1="$1"
local -n arr2="$2"
local -A set1
local result=()
for item in "${arr1[@]}"; do
set1[$item]=1
done
for item in "${arr2[@]}"; do
if [[ -n "${set1[$item]:-}" ]]; then
result+=("$item")
fi
done
printf '%s\n' "${result[@]}"
}
array_difference() {
local -n arr1="$1"
local -n arr2="$2"
local -A set2
local result=()
for item in "${arr2[@]}"; do
set2[$item]=1
done
for item in "${arr1[@]}"; do
if [[ -z "${set2[$item]:-}" ]]; then
result+=("$item")
fi
done
printf '%s\n' "${result[@]}"
}
Resources
Master bash arrays for efficient data manipulation and avoid common pitfalls like word splitting and subshell overhead.