Comprehensive Reference Guide — Indexed arrays, associative arrays, slicing, iteration, sorting, searching, and functions
Bash supports two types of arrays: indexed arrays (integer keys starting from 0) and associative arrays (string keys, Bash 4.0+). Arrays are essential for handling lists of files, configuration options, command arguments, and structured data without relying on external tools.
Indexed Arrays: Traditional arrays with numeric indices. Elements accessed by position.
Associative Arrays: Key-value pairs like dictionaries/hashes. Require explicit declaration with declare -A.
| Operation | Indexed Array | Associative Array |
|---|---|---|
| Declare | declare -a arr |
declare -A arr |
| Initialize | arr=(a b c) |
arr=([key1]=val1 [key2]=val2) |
| Assign element | arr[0]="value" |
arr[key]="value" |
| Append | arr+=("new") |
arr+=([key]=val) |
| From command | arr=($(cmd)) |
N/A |
| Read lines | readarray -t arr < file |
N/A |
| Syntax | Description | Example |
|---|---|---|
${arr[index]} |
Single element | ${arr[0]} |
${arr[@]} |
All elements (separate words) | for i in "${arr[@]}" |
${arr[*]} |
All elements (single string) | echo "${arr[*]}" |
${!arr[@]} |
All indices/keys | for k in "${!arr[@]}" |
${#arr[@]} |
Array length | len=${#arr[@]} |
${#arr[i]} |
Element length | ${#arr[0]} |
| Syntax | Description | Example |
|---|---|---|
${arr[@]:start} |
From index to end | ${arr[@]:2} |
${arr[@]:start:count} |
Count elements from start | ${arr[@]:1:3} |
${arr[@]: -n} |
Last n elements | ${arr[@]: -2} |
| Operation | Syntax | Example |
|---|---|---|
| Unset element | unset 'arr[index]' |
unset 'arr[2]' |
| Unset array | unset arr |
unset myarray |
| Replace pattern | ${arr[@]/pat/rep} |
${arr[@]/old/new} |
| Copy array | new=("${old[@]}") |
copy=("${arr[@]}") |
| Merge arrays | merged=("${a[@]}" "${b[@]}") |
all=("${x[@]}" "${y[@]}") |
"${arr[@]}" with quotes to preserve elements with spaces. Without quotes, elements get word-split.
Different ways to create and populate both indexed and associative arrays.
#!/bin/bash
# Array declaration and initialization
echo "=== Indexed Arrays ==="
# Method 1: Direct assignment
fruits=("apple" "banana" "cherry" "date")
echo "Direct: ${fruits[*]}"
# Method 2: Individual assignment
colors[0]="red"
colors[1]="green"
colors[2]="blue"
echo "Individual: ${colors[*]}"
# Method 3: Non-contiguous indices
sparse[0]="first"
sparse[5]="sixth"
sparse[10]="eleventh"
echo "Sparse: ${sparse[*]}"
echo "Sparse indices: ${!sparse[*]}"
# Method 4: From command output
files=($(ls *.html 2>/dev/null || echo "none"))
echo "From ls: ${files[*]}"
# Method 5: Read from file (preserves lines)
echo -e "line one\nline two\nline three" > /tmp/test.txt
readarray -t lines < /tmp/test.txt
echo "From file: ${lines[*]}"
rm /tmp/test.txt
# Method 6: With indices
indexed=([0]="zero" [2]="two" [4]="four")
echo "With indices: ${indexed[*]}"
echo ""
echo "=== Associative Arrays (Bash 4+) ==="
# Must declare first
declare -A user
user[name]="John"
user[age]="30"
user[city]="New York"
echo "User: name=${user[name]}, age=${user[age]}, city=${user[city]}"
# Initialize all at once
declare -A config=(
[host]="localhost"
[port]="3306"
[database]="myapp"
[user]="admin"
)
echo "Config: host=${config[host]}, port=${config[port]}"
# Keys with spaces (quote them)
declare -A info
info["full name"]="John Doe"
info["home address"]="123 Main St"
echo "Full name: ${info["full name"]}"
echo ""
echo "=== Array Properties ==="
arr=("one" "two" "three" "four" "five")
echo "Array: ${arr[*]}"
echo "Length: ${#arr[@]}"
echo "Indices: ${!arr[@]}"
echo "First: ${arr[0]}"
echo "Last: ${arr[-1]}"
echo "Element 2 length: ${#arr[2]}"
echo ""
echo "=== Empty vs Unset ==="
empty=()
echo "Empty array length: ${#empty[@]}"
unset undefined
echo "Unset array length: ${#undefined[@]}"
# Check if array exists
if [[ -v arr[@] ]]; then
echo "arr is defined"
fi
if [[ ${#empty[@]} -eq 0 ]]; then
echo "empty has no elements"
fi
Different ways to loop through array elements and indices.
#!/bin/bash
# Array iteration methods
arr=("apple" "banana" "cherry" "date" "elderberry")
echo "=== Iterate Over Elements ==="
# Method 1: For-each (recommended)
echo "For-each:"
for item in "${arr[@]}"; do
echo " $item"
done
# Method 2: C-style for loop
echo ""
echo "C-style loop:"
for ((i=0; i<${#arr[@]}; i++)); do
echo " [$i] = ${arr[$i]}"
done
# Method 3: Iterate over indices
echo ""
echo "By index:"
for i in "${!arr[@]}"; do
echo " arr[$i] = ${arr[$i]}"
done
# Method 4: While loop with counter
echo ""
echo "While loop:"
i=0
while [[ $i -lt ${#arr[@]} ]]; do
echo " $i: ${arr[$i]}"
((i++))
done
echo ""
echo "=== Iterate Associative Array ==="
declare -A config=(
[host]="localhost"
[port]="3306"
[user]="admin"
[pass]="secret"
)
echo "Key-value pairs:"
for key in "${!config[@]}"; do
echo " $key = ${config[$key]}"
done
echo ""
echo "=== Iterate with Index Position ==="
arr=("first" "second" "third")
# Using enumerate pattern
i=1
for item in "${arr[@]}"; do
echo " $i. $item"
((i++))
done
echo ""
echo "=== Iterate in Reverse ==="
arr=("A" "B" "C" "D" "E")
echo "Reverse order:"
for ((i=${#arr[@]}-1; i>=0; i--)); do
echo " ${arr[$i]}"
done
echo ""
echo "=== Iterate Over Slices ==="
arr=("a" "b" "c" "d" "e" "f" "g")
echo "Elements 2-4:"
for item in "${arr[@]:2:3}"; do
echo " $item"
done
echo ""
echo "Last 3 elements:"
for item in "${arr[@]: -3}"; do
echo " $item"
done
echo ""
echo "=== Safe Iteration (Empty Check) ==="
empty_arr=()
if [[ ${#empty_arr[@]} -gt 0 ]]; then
for item in "${empty_arr[@]}"; do
echo "$item"
done
else
echo "Array is empty, nothing to iterate"
fi
echo ""
echo "=== Iterate with Conditional ==="
numbers=(1 2 3 4 5 6 7 8 9 10)
echo "Even numbers:"
for n in "${numbers[@]}"; do
if ((n % 2 == 0)); then
echo " $n"
fi
done
Appending, prepending, inserting, and removing array elements.
#!/bin/bash
# Adding and removing array elements
echo "=== Appending Elements ==="
arr=("one" "two" "three")
echo "Initial: ${arr[*]}"
# Append single element
arr+=("four")
echo "After +=: ${arr[*]}"
# Append multiple elements
arr+=("five" "six")
echo "After += multiple: ${arr[*]}"
# Append at specific index
arr[10]="ten"
echo "After [10]=: ${arr[*]}"
echo "Indices: ${!arr[*]}"
echo ""
echo "=== Prepending Elements ==="
arr=("two" "three" "four")
echo "Initial: ${arr[*]}"
# Prepend by creating new array
arr=("one" "${arr[@]}")
echo "After prepend: ${arr[*]}"
# Prepend multiple
arr=("minus-one" "zero" "${arr[@]}")
echo "After prepend multiple: ${arr[*]}"
echo ""
echo "=== Inserting at Position ==="
arr=("a" "b" "d" "e")
echo "Initial: ${arr[*]}"
# Insert "c" at position 2
pos=2
arr=("${arr[@]:0:pos}" "c" "${arr[@]:pos}")
echo "After insert at $pos: ${arr[*]}"
# Insert function
insert_at() {
local -n array=$1
local pos=$2
local value=$3
array=("${array[@]:0:pos}" "$value" "${array[@]:pos}")
}
insert_at arr 1 "a.5"
echo "After insert_at: ${arr[*]}"
echo ""
echo "=== Removing Elements ==="
# Remove by index (leaves gap)
arr=("a" "b" "c" "d" "e")
echo "Initial: ${arr[*]}"
unset 'arr[2]'
echo "After unset [2]: ${arr[*]}"
echo "Indices now: ${!arr[*]}"
# Re-index after removal
arr=("${arr[@]}")
echo "After re-index: ${arr[*]}"
echo "Indices now: ${!arr[*]}"
echo ""
echo "=== Remove by Value ==="
arr=("apple" "banana" "cherry" "banana" "date")
echo "Initial: ${arr[*]}"
# Remove first occurrence
remove_first() {
local -n array=$1
local value=$2
local new_arr=()
local removed=false
for item in "${array[@]}"; do
if [[ "$item" == "$value" && "$removed" == false ]]; then
removed=true
else
new_arr+=("$item")
fi
done
array=("${new_arr[@]}")
}
remove_first arr "banana"
echo "Remove first 'banana': ${arr[*]}"
# Remove all occurrences
arr=("apple" "banana" "cherry" "banana" "date")
remove_all() {
local -n array=$1
local value=$2
local new_arr=()
for item in "${array[@]}"; do
if [[ "$item" != "$value" ]]; then
new_arr+=("$item")
fi
done
array=("${new_arr[@]}")
}
remove_all arr "banana"
echo "Remove all 'banana': ${arr[*]}"
echo ""
echo "=== Pop and Shift ==="
arr=("first" "second" "third" "fourth" "fifth")
echo "Initial: ${arr[*]}"
# Pop (remove last)
last="${arr[-1]}"
unset 'arr[-1]'
echo "Popped '$last': ${arr[*]}"
# Shift (remove first)
first="${arr[0]}"
arr=("${arr[@]:1}")
echo "Shifted '$first': ${arr[*]}"
echo ""
echo "=== Clear Array ==="
arr=("a" "b" "c")
echo "Before clear: ${arr[*]} (${#arr[@]} elements)"
arr=()
echo "After clear: ${arr[*]} (${#arr[@]} elements)"
unset 'arr[i]' removes the element but doesn't re-index. Use arr=("${arr[@]}") to re-index if needed.
Finding elements, checking membership, and getting indices.
#!/bin/bash
# Searching arrays
arr=("apple" "banana" "cherry" "date" "elderberry" "banana")
echo "=== Check If Element Exists ==="
echo "Array: ${arr[*]}"
# Method 1: Loop search
contains() {
local -n array=$1
local value=$2
for item in "${array[@]}"; do
if [[ "$item" == "$value" ]]; then
return 0
fi
done
return 1
}
if contains arr "cherry"; then
echo "'cherry' found"
else
echo "'cherry' not found"
fi
if contains arr "grape"; then
echo "'grape' found"
else
echo "'grape' not found"
fi
# Method 2: Pattern matching (works for simple cases)
if [[ " ${arr[*]} " =~ " banana " ]]; then
echo "'banana' found (pattern match)"
fi
echo ""
echo "=== Find Index of Element ==="
# Find first occurrence
find_index() {
local -n array=$1
local value=$2
for i in "${!array[@]}"; do
if [[ "${array[$i]}" == "$value" ]]; then
echo "$i"
return 0
fi
done
echo "-1"
return 1
}
idx=$(find_index arr "cherry")
echo "Index of 'cherry': $idx"
idx=$(find_index arr "grape")
echo "Index of 'grape': $idx"
echo ""
echo "=== Find All Indices ==="
# Find all occurrences
find_all_indices() {
local -n array=$1
local value=$2
local indices=()
for i in "${!array[@]}"; do
if [[ "${array[$i]}" == "$value" ]]; then
indices+=("$i")
fi
done
echo "${indices[*]}"
}
echo "All indices of 'banana': $(find_all_indices arr "banana")"
echo ""
echo "=== Count Occurrences ==="
count_occurrences() {
local -n array=$1
local value=$2
local count=0
for item in "${array[@]}"; do
if [[ "$item" == "$value" ]]; then
((count++))
fi
done
echo "$count"
}
echo "Count of 'banana': $(count_occurrences arr "banana")"
echo "Count of 'apple': $(count_occurrences arr "apple")"
echo ""
echo "=== Search with Pattern ==="
files=("report.txt" "data.csv" "image.png" "notes.txt" "photo.jpg")
echo "Files: ${files[*]}"
echo ""
echo "Text files (.txt):"
for f in "${files[@]}"; do
if [[ "$f" == *.txt ]]; then
echo " $f"
fi
done
# Using grep on array
echo ""
echo "Files containing 'o':"
printf '%s\n' "${files[@]}" | grep 'o'
echo ""
echo "=== Search Associative Array ==="
declare -A users=(
[alice]="admin"
[bob]="user"
[charlie]="admin"
[dave]="user"
)
# Find keys by value
echo "Admin users:"
for user in "${!users[@]}"; do
if [[ "${users[$user]}" == "admin" ]]; then
echo " $user"
fi
done
# Check if key exists
if [[ -v users[alice] ]]; then
echo "alice exists with role: ${users[alice]}"
fi
echo ""
echo "=== Binary Search (Sorted Array) ==="
# For large sorted arrays
binary_search() {
local -n array=$1
local target=$2
local low=0
local high=$((${#array[@]} - 1))
while ((low <= high)); do
local mid=$(( (low + high) / 2 ))
if [[ "${array[$mid]}" == "$target" ]]; then
echo "$mid"
return 0
elif [[ "${array[$mid]}" < "$target" ]]; then
low=$((mid + 1))
else
high=$((mid - 1))
fi
done
echo "-1"
return 1
}
sorted=("alpha" "beta" "delta" "gamma" "omega")
echo "Sorted array: ${sorted[*]}"
echo "Binary search 'gamma': index $(binary_search sorted "gamma")"
echo "Binary search 'epsilon': index $(binary_search sorted "epsilon")"
Sorting array elements alphabetically, numerically, and in custom order.
#!/bin/bash
# Sorting arrays
echo "=== Alphabetical Sort ==="
fruits=("banana" "apple" "cherry" "date" "elderberry")
echo "Original: ${fruits[*]}"
# Sort using readarray and sort
sorted=($(printf '%s\n' "${fruits[@]}" | sort))
echo "Sorted: ${sorted[*]}"
# Reverse sort
reversed=($(printf '%s\n' "${fruits[@]}" | sort -r))
echo "Reversed: ${reversed[*]}"
echo ""
echo "=== Numerical Sort ==="
numbers=(10 5 3 8 1 9 2 7 4 6)
echo "Original: ${numbers[*]}"
# Sort numerically
sorted_nums=($(printf '%s\n' "${numbers[@]}" | sort -n))
echo "Sorted: ${sorted_nums[*]}"
# Reverse numerical
reversed_nums=($(printf '%s\n' "${numbers[@]}" | sort -rn))
echo "Reversed: ${reversed_nums[*]}"
echo ""
echo "=== Sort Preserving Elements with Spaces ==="
items=("red apple" "green banana" "yellow cherry")
echo "Original:"
printf ' "%s"\n' "${items[@]}"
# Use readarray with null delimiter
readarray -t sorted_items < <(printf '%s\n' "${items[@]}" | sort)
echo "Sorted:"
printf ' "%s"\n' "${sorted_items[@]}"
echo ""
echo "=== Case-Insensitive Sort ==="
words=("Apple" "banana" "Cherry" "date")
echo "Original: ${words[*]}"
sorted_ci=($(printf '%s\n' "${words[@]}" | sort -f))
echo "Case-insensitive: ${sorted_ci[*]}"
echo ""
echo "=== Sort by Field ==="
# Data: name:age
people=("alice:30" "bob:25" "charlie:35" "dave:28")
echo "Original: ${people[*]}"
# Sort by name (field 1)
by_name=($(printf '%s\n' "${people[@]}" | sort -t: -k1))
echo "By name: ${by_name[*]}"
# Sort by age (field 2, numeric)
by_age=($(printf '%s\n' "${people[@]}" | sort -t: -k2 -n))
echo "By age: ${by_age[*]}"
echo ""
echo "=== Unique Sort ==="
with_dupes=("apple" "banana" "apple" "cherry" "banana" "date")
echo "With dupes: ${with_dupes[*]}"
unique=($(printf '%s\n' "${with_dupes[@]}" | sort -u))
echo "Unique: ${unique[*]}"
echo ""
echo "=== Version Sort ==="
versions=("1.10.0" "1.2.0" "1.9.0" "2.0.0" "1.1.0")
echo "Original: ${versions[*]}"
sorted_ver=($(printf '%s\n' "${versions[@]}" | sort -V))
echo "Version: ${sorted_ver[*]}"
echo ""
echo "=== Custom Sort Function ==="
# Sort by string length
arr=("a" "bbb" "cc" "dddd" "eeeee")
echo "Original: ${arr[*]}"
# Add length prefix, sort, remove prefix
by_length=()
for item in "${arr[@]}"; do
by_length+=("$(printf '%05d:%s' ${#item} "$item")")
done
sorted_by_length=($(printf '%s\n' "${by_length[@]}" | sort | cut -d: -f2))
echo "By length: ${sorted_by_length[*]}"
echo ""
echo "=== In-Place Sort Pattern ==="
arr=("zebra" "apple" "mango")
echo "Before: ${arr[*]}"
# Sort and reassign
readarray -t arr < <(printf '%s\n' "${arr[@]}" | sort)
echo "After: ${arr[*]}"
echo ""
echo "=== Shuffle Array ==="
arr=("A" "B" "C" "D" "E")
echo "Original: ${arr[*]}"
shuffled=($(printf '%s\n' "${arr[@]}" | sort -R))
echo "Shuffled: ${shuffled[*]}"
Map, filter, and reduce operations on arrays.
#!/bin/bash
# Array transformation operations
echo "=== Map: Transform Each Element ==="
numbers=(1 2 3 4 5)
echo "Original: ${numbers[*]}"
# Square each number
squared=()
for n in "${numbers[@]}"; do
squared+=($((n * n)))
done
echo "Squared: ${squared[*]}"
# Double each number
doubled=("${numbers[@]/#/2*}") # Won't work - need loop
doubled=()
for n in "${numbers[@]}"; do
doubled+=($((n * 2)))
done
echo "Doubled: ${doubled[*]}"
echo ""
echo "=== Map Strings ==="
names=("john" "jane" "bob")
echo "Original: ${names[*]}"
# Uppercase all
upper=()
for name in "${names[@]}"; do
upper+=("${name^^}")
done
echo "Upper: ${upper[*]}"
# Add prefix
prefixed=("${names[@]/#/user_}")
echo "Prefixed: ${prefixed[*]}"
# Add suffix
suffixed=("${names[@]/%/_account}")
echo "Suffixed: ${suffixed[*]}"
echo ""
echo "=== Filter: Select Elements ==="
numbers=(1 2 3 4 5 6 7 8 9 10)
echo "Original: ${numbers[*]}"
# Filter even numbers
evens=()
for n in "${numbers[@]}"; do
if ((n % 2 == 0)); then
evens+=("$n")
fi
done
echo "Evens: ${evens[*]}"
# Filter greater than 5
gt_five=()
for n in "${numbers[@]}"; do
if ((n > 5)); then
gt_five+=("$n")
fi
done
echo "GT 5: ${gt_five[*]}"
echo ""
echo "=== Filter Strings by Pattern ==="
files=("report.txt" "data.csv" "image.png" "notes.txt" "photo.jpg")
echo "Files: ${files[*]}"
# Filter .txt files
txt_files=()
for f in "${files[@]}"; do
if [[ "$f" == *.txt ]]; then
txt_files+=("$f")
fi
done
echo "TXT files: ${txt_files[*]}"
echo ""
echo "=== Reduce: Aggregate Values ==="
numbers=(1 2 3 4 5)
echo "Numbers: ${numbers[*]}"
# Sum
sum=0
for n in "${numbers[@]}"; do
((sum += n))
done
echo "Sum: $sum"
# Product
product=1
for n in "${numbers[@]}"; do
((product *= n))
done
echo "Product: $product"
# Min
min=${numbers[0]}
for n in "${numbers[@]}"; do
((n < min)) && min=$n
done
echo "Min: $min"
# Max
max=${numbers[0]}
for n in "${numbers[@]}"; do
((n > max)) && max=$n
done
echo "Max: $max"
echo ""
echo "=== Join/Flatten ==="
arr=("one" "two" "three")
echo "Array: ${arr[*]}"
# Join with delimiter
joined=$(IFS=','; echo "${arr[*]}")
echo "Comma joined: $joined"
# Join with custom delimiter
join_with() {
local delim=$1
shift
local result="$1"
shift
for item in "$@"; do
result+="${delim}${item}"
done
echo "$result"
}
echo "Pipe joined: $(join_with '|' "${arr[@]}")"
echo ""
echo "=== Zip Two Arrays ==="
keys=("name" "age" "city")
values=("John" "30" "NYC")
declare -A zipped
for i in "${!keys[@]}"; do
zipped[${keys[$i]}]="${values[$i]}"
done
echo "Zipped:"
for k in "${!zipped[@]}"; do
echo " $k = ${zipped[$k]}"
done
Different techniques for passing arrays to and returning arrays from functions.
#!/bin/bash
# Passing arrays to functions
echo "=== Method 1: Pass by Name Reference (Bash 4.3+) ==="
process_array() {
local -n arr=$1 # nameref
echo " Length: ${#arr[@]}"
echo " Elements: ${arr[*]}"
# Can modify original
arr+=("added")
}
my_array=("one" "two" "three")
echo "Before:"
echo " ${my_array[*]}"
echo "In function:"
process_array my_array
echo "After:"
echo " ${my_array[*]}"
echo ""
echo "=== Method 2: Pass All Elements ==="
sum_elements() {
local sum=0
for n in "$@"; do
((sum += n))
done
echo "$sum"
}
numbers=(10 20 30 40 50)
total=$(sum_elements "${numbers[@]}")
echo "Numbers: ${numbers[*]}"
echo "Sum: $total"
echo ""
echo "=== Method 3: Return Array via Echo ==="
double_all() {
local result=()
for n in "$@"; do
result+=($((n * 2)))
done
echo "${result[*]}"
}
numbers=(1 2 3 4 5)
read -ra doubled <<< "$(double_all "${numbers[@]}")"
echo "Original: ${numbers[*]}"
echo "Doubled: ${doubled[*]}"
echo ""
echo "=== Method 4: Return via Global Variable ==="
RESULT_ARRAY=()
get_even_numbers() {
RESULT_ARRAY=()
for n in "$@"; do
if ((n % 2 == 0)); then
RESULT_ARRAY+=("$n")
fi
done
}
numbers=(1 2 3 4 5 6 7 8 9 10)
get_even_numbers "${numbers[@]}"
echo "Numbers: ${numbers[*]}"
echo "Evens: ${RESULT_ARRAY[*]}"
echo ""
echo "=== Associative Array by Reference ==="
print_config() {
local -n config=$1
echo " Configuration:"
for key in "${!config[@]}"; do
printf " %-10s = %s\n" "$key" "${config[$key]}"
done
}
declare -A app_config=(
[host]="localhost"
[port]="8080"
[debug]="true"
)
print_config app_config
echo ""
echo "=== Multiple Array Parameters ==="
merge_arrays() {
local -n arr1=$1
local -n arr2=$2
local -n result=$3
result=("${arr1[@]}" "${arr2[@]}")
}
first=("a" "b" "c")
second=("d" "e" "f")
merged=()
merge_arrays first second merged
echo "First: ${first[*]}"
echo "Second: ${second[*]}"
echo "Merged: ${merged[*]}"
echo ""
echo "=== Practical: Array Processing Pipeline ==="
# Filter, then map, then reduce
pipeline() {
local -n input=$1
local threshold=$2
# Filter > threshold
local filtered=()
for n in "${input[@]}"; do
if ((n > threshold)); then
filtered+=("$n")
fi
done
# Map: square
local mapped=()
for n in "${filtered[@]}"; do
mapped+=($((n * n)))
done
# Reduce: sum
local sum=0
for n in "${mapped[@]}"; do
((sum += n))
done
echo "Filtered (>$threshold): ${filtered[*]}"
echo "Squared: ${mapped[*]}"
echo "Sum: $sum"
}
data=(1 2 3 4 5 6 7 8 9 10)
echo "Data: ${data[*]}"
echo ""
pipeline data 5
local -n ref=$1 creates a reference to the array passed by name. Changes affect the original. This is the cleanest approach for modern Bash.
Advanced patterns for working with associative arrays (dictionaries/hashes).
#!/bin/bash
# Associative array patterns
echo "=== Basic Associative Array ==="
declare -A user=(
[name]="John Doe"
[email]="john@example.com"
[role]="admin"
[active]="true"
)
echo "User:"
for key in "${!user[@]}"; do
printf " %-10s = %s\n" "$key" "${user[$key]}"
done
echo ""
echo "=== Check Key Existence ==="
# Method 1: -v flag (Bash 4.2+)
if [[ -v user[email] ]]; then
echo "Key 'email' exists: ${user[email]}"
fi
if [[ ! -v user[phone] ]]; then
echo "Key 'phone' does not exist"
fi
# Method 2: Check if empty (less reliable)
if [[ -n "${user[name]}" ]]; then
echo "Key 'name' has value: ${user[name]}"
fi
echo ""
echo "=== Default Values ==="
# Get with default
get_value() {
local -n arr=$1
local key=$2
local default=$3
if [[ -v arr[$key] ]]; then
echo "${arr[$key]}"
else
echo "$default"
fi
}
echo "Name: $(get_value user name 'Unknown')"
echo "Phone: $(get_value user phone 'Not provided')"
echo ""
echo "=== Nested Structures (Simulated) ==="
# Bash doesn't support nested arrays directly
# Simulate with naming convention
declare -A data
data[user.name]="John"
data[user.email]="john@example.com"
data[server.host]="localhost"
data[server.port]="8080"
echo "Simulated nested:"
for key in "${!data[@]}"; do
echo " $key = ${data[$key]}"
done
# Get all "user" keys
echo ""
echo "User properties:"
for key in "${!data[@]}"; do
if [[ $key == user.* ]]; then
prop="${key#user.}"
echo " $prop = ${data[$key]}"
fi
done
echo ""
echo "=== Count and Group ==="
# Count occurrences
declare -A counts
words=("apple" "banana" "apple" "cherry" "banana" "apple")
for word in "${words[@]}"; do
((counts[$word]++))
done
echo "Word counts:"
for word in "${!counts[@]}"; do
echo " $word: ${counts[$word]}"
done
echo ""
echo "=== Invert Key-Value ==="
declare -A original=(
[a]="1"
[b]="2"
[c]="3"
)
declare -A inverted
for key in "${!original[@]}"; do
inverted[${original[$key]}]="$key"
done
echo "Original:"
for k in "${!original[@]}"; do echo " $k → ${original[$k]}"; done
echo "Inverted:"
for k in "${!inverted[@]}"; do echo " $k → ${inverted[$k]}"; done
echo ""
echo "=== Merge Associative Arrays ==="
declare -A defaults=(
[host]="localhost"
[port]="3306"
[timeout]="30"
)
declare -A overrides=(
[port]="5432"
[database]="myapp"
)
declare -A merged
# Copy defaults
for key in "${!defaults[@]}"; do
merged[$key]="${defaults[$key]}"
done
# Apply overrides
for key in "${!overrides[@]}"; do
merged[$key]="${overrides[$key]}"
done
echo "Merged config:"
for key in "${!merged[@]}"; do
printf " %-10s = %s\n" "$key" "${merged[$key]}"
done
echo ""
echo "=== Parse Key=Value to Associative Array ==="
declare -A config
config_text="
host=localhost
port=8080
debug=true
name=My App
"
while IFS='=' read -r key value; do
# Skip empty lines
[[ -z "$key" ]] && continue
# Trim whitespace
key="${key// /}"
config[$key]="$value"
done <<< "$config_text"
echo "Parsed config:"
for key in "${!config[@]}"; do
echo " $key = ${config[$key]}"
done
echo ""
echo "=== Export to JSON-like Format ==="
to_json() {
local -n arr=$1
local first=true
echo -n "{"
for key in "${!arr[@]}"; do
if [[ "$first" == true ]]; then
first=false
else
echo -n ", "
fi
echo -n "\"$key\": \"${arr[$key]}\""
done
echo "}"
}
declare -A data=([name]="John" [age]="30" [city]="NYC")
to_json data
Processing command line arguments using arrays.
#!/bin/bash
# Command line argument processing with arrays
echo "=== Basic Argument Array ==="
# Simulated arguments for demonstration
set -- "-v" "--config" "/etc/app.conf" "file1.txt" "file2.txt"
echo "All args: $@"
echo "Arg count: $#"
echo ""
# Store in array
args=("$@")
echo "As array: ${args[*]}"
echo "First arg: ${args[0]}"
echo "Last arg: ${args[-1]}"
echo ""
echo "=== Parse Options and Arguments ==="
# Reset args
set -- "-v" "--config" "/etc/app.conf" "--output" "out.txt" "input1.txt" "input2.txt"
declare -A options
files=()
verbose=false
while [[ $# -gt 0 ]]; do
case "$1" in
-v|--verbose)
verbose=true
shift
;;
--config)
options[config]="$2"
shift 2
;;
--output)
options[output]="$2"
shift 2
;;
-*)
echo "Unknown option: $1"
shift
;;
*)
files+=("$1")
shift
;;
esac
done
echo "Options:"
echo " verbose: $verbose"
for key in "${!options[@]}"; do
echo " $key: ${options[$key]}"
done
echo ""
echo "Files:"
for f in "${files[@]}"; do
echo " $f"
done
echo ""
echo "=== getopt-style Short Options ==="
# Reset
set -- "-vf" "config.txt" "-o" "output.txt" "input.txt"
verbose=false
force=false
config=""
output=""
inputs=()
while [[ $# -gt 0 ]]; do
case "$1" in
-*)
# Handle combined short options
opts="${1#-}"
while [[ -n "$opts" ]]; do
opt="${opts:0:1}"
opts="${opts:1}"
case "$opt" in
v) verbose=true ;;
f) force=true ;;
o) output="$2"; shift ;;
*) echo "Unknown: -$opt" ;;
esac
done
shift
;;
*)
inputs+=("$1")
shift
;;
esac
done
echo "Verbose: $verbose"
echo "Force: $force"
echo "Output: $output"
echo "Inputs: ${inputs[*]}"
echo ""
echo "=== Positional Parameters as Array ==="
# Function that takes variable arguments
process_files() {
local files=("$@")
echo "Processing ${#files[@]} files:"
for f in "${files[@]}"; do
echo " - $f"
done
}
process_files "doc1.txt" "doc2.txt" "doc3.txt"
echo ""
echo "=== Build Command Array ==="
# Safe way to build commands with arguments
cmd=("ls" "-la" "--color=auto")
# Add conditional options
if [[ "$verbose" == true ]]; then
cmd+=("-v")
fi
# Add files
cmd+=("/tmp" "/var")
echo "Command: ${cmd[*]}"
echo ""
echo "Execute:"
"${cmd[@]}"
echo ""
echo "=== Shift Through Arguments ==="
# Reset
set -- "first" "second" "third" "fourth"
echo "Processing arguments:"
while [[ $# -gt 0 ]]; do
echo " Current: $1 (remaining: $#)"
shift
done
"${cmd[@]}". This preserves arguments with spaces and prevents word splitting issues.
A reusable library of array utility functions.
#!/bin/bash
# Complete array utility library
#######################################
# Basic Operations
#######################################
# Get array length
arr_length() {
local -n arr=$1
echo "${#arr[@]}"
}
# Check if empty
arr_empty() {
local -n arr=$1
[[ ${#arr[@]} -eq 0 ]]
}
# Get first element
arr_first() {
local -n arr=$1
echo "${arr[0]}"
}
# Get last element
arr_last() {
local -n arr=$1
echo "${arr[-1]}"
}
#######################################
# Search Operations
#######################################
# Check if contains element
arr_contains() {
local -n arr=$1
local value=$2
for item in "${arr[@]}"; do
[[ "$item" == "$value" ]] && return 0
done
return 1
}
# Find index of element
arr_index_of() {
local -n arr=$1
local value=$2
for i in "${!arr[@]}"; do
if [[ "${arr[$i]}" == "$value" ]]; then
echo "$i"
return 0
fi
done
echo "-1"
return 1
}
# Count occurrences
arr_count() {
local -n arr=$1
local value=$2
local count=0
for item in "${arr[@]}"; do
[[ "$item" == "$value" ]] && ((count++))
done
echo "$count"
}
#######################################
# Modification
#######################################
# Push element
arr_push() {
local -n arr=$1
shift
arr+=("$@")
}
# Pop last element
arr_pop() {
local -n arr=$1
local last="${arr[-1]}"
unset 'arr[-1]'
echo "$last"
}
# Shift first element
arr_shift() {
local -n arr=$1
local first="${arr[0]}"
arr=("${arr[@]:1}")
echo "$first"
}
# Unshift (prepend)
arr_unshift() {
local -n arr=$1
shift
arr=("$@" "${arr[@]}")
}
# Remove by value (first occurrence)
arr_remove() {
local -n arr=$1
local value=$2
local new=()
local removed=false
for item in "${arr[@]}"; do
if [[ "$item" == "$value" && "$removed" == false ]]; then
removed=true
else
new+=("$item")
fi
done
arr=("${new[@]}")
}
# Remove all occurrences
arr_remove_all() {
local -n arr=$1
local value=$2
local new=()
for item in "${arr[@]}"; do
[[ "$item" != "$value" ]] && new+=("$item")
done
arr=("${new[@]}")
}
#######################################
# Transformation
#######################################
# Reverse array
arr_reverse() {
local -n arr=$1
local new=()
for ((i=${#arr[@]}-1; i>=0; i--)); do
new+=("${arr[$i]}")
done
arr=("${new[@]}")
}
# Get unique elements
arr_unique() {
local -n arr=$1
local -A seen
local new=()
for item in "${arr[@]}"; do
if [[ ! -v seen[$item] ]]; then
seen[$item]=1
new+=("$item")
fi
done
arr=("${new[@]}")
}
# Sort array
arr_sort() {
local -n arr=$1
readarray -t arr < <(printf '%s\n' "${arr[@]}" | sort)
}
# Join to string
arr_join() {
local -n arr=$1
local delim=$2
local result="${arr[0]}"
for ((i=1; i<${#arr[@]}; i++)); do
result+="${delim}${arr[$i]}"
done
echo "$result"
}
#######################################
# Aggregation
#######################################
# Sum numeric array
arr_sum() {
local -n arr=$1
local sum=0
for n in "${arr[@]}"; do
((sum += n))
done
echo "$sum"
}
# Min value
arr_min() {
local -n arr=$1
local min="${arr[0]}"
for n in "${arr[@]}"; do
((n < min)) && min=$n
done
echo "$min"
}
# Max value
arr_max() {
local -n arr=$1
local max="${arr[0]}"
for n in "${arr[@]}"; do
((n > max)) && max=$n
done
echo "$max"
}
#######################################
# Demo
#######################################
echo "========================================"
echo " Array Library Demo"
echo "========================================"
echo ""
echo "=== Basic Operations ==="
arr=("one" "two" "three" "four" "five")
echo "Array: ${arr[*]}"
echo "Length: $(arr_length arr)"
echo "First: $(arr_first arr)"
echo "Last: $(arr_last arr)"
echo ""
echo "=== Search Operations ==="
arr_contains arr "three" && echo "Contains 'three': yes"
echo "Index of 'three': $(arr_index_of arr "three")"
echo ""
echo "=== Modification ==="
arr=("a" "b" "c")
echo "Initial: ${arr[*]}"
arr_push arr "d" "e"
echo "After push d,e: ${arr[*]}"
popped=$(arr_pop arr)
echo "Popped '$popped': ${arr[*]}"
shifted=$(arr_shift arr)
echo "Shifted '$shifted': ${arr[*]}"
arr_unshift arr "x" "y"
echo "Unshift x,y: ${arr[*]}"
echo ""
echo "=== Transformation ==="
arr=("c" "a" "b" "a" "c")
echo "Initial: ${arr[*]}"
arr_unique arr
echo "Unique: ${arr[*]}"
arr_sort arr
echo "Sorted: ${arr[*]}"
arr_reverse arr
echo "Reversed: ${arr[*]}"
echo ""
echo "=== Aggregation ==="
nums=(5 2 8 1 9 3)
echo "Numbers: ${nums[*]}"
echo "Sum: $(arr_sum nums)"
echo "Min: $(arr_min nums)"
echo "Max: $(arr_max nums)"
echo ""
echo "=== Join ==="
arr=("one" "two" "three")
echo "Comma: $(arr_join arr ',')"
echo "Pipe: $(arr_join arr ' | ')"
echo ""
echo "========================================"
# Declaration
arr=(a b c) # Indexed array
declare -A arr # Associative array (required)
# Access
${arr[0]} # Single element
${arr[@]} # All elements (separate)
${arr[*]} # All elements (single string)
${!arr[@]} # All indices/keys
${#arr[@]} # Length
${arr[-1]} # Last element
# Slicing
${arr[@]:start:count} # Slice from start
${arr[@]: -n} # Last n elements
# Modify
arr+=("new") # Append
arr[i]="value" # Set element
unset 'arr[i]' # Remove element
arr=() # Clear array
# Iterate
for item in "${arr[@]}"; do ... done
for i in "${!arr[@]}"; do ... done
| Task | Code |
|---|---|
| Check if empty | [[ ${#arr[@]} -eq 0 ]] |
| Copy array | new=("${old[@]}") |
| Merge arrays | all=("${a[@]}" "${b[@]}") |
| Read file to array | readarray -t arr < file |
| Sort array | readarray -t arr < <(printf '%s\n' "${arr[@]}" | sort) |
| Join with comma | $(IFS=','; echo "${arr[*]}") |
| Check contains | [[ " ${arr[*]} " =~ " value " ]] |
| Check key exists | [[ -v arr[key] ]] |
"${arr[@]}"[@] not [*] in for loopsdeclare -Alocal -n for passing arrays to functionsreadarray -t to read file lines${arr[@]} vs "${arr[@]}"arr=($string) which word-splitsunset to re-index[*] in for loops (joins elements)