📚 Bash Array Operations

Comprehensive Reference Guide — Indexed arrays, associative arrays, slicing, iteration, sorting, searching, and functions

📋 Table of Contents

Overview

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.

Array Types

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.

Basic Operations Reference

Declaration and Assignment

OperationIndexed ArrayAssociative 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

Accessing Elements

SyntaxDescriptionExample
${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]}

Slicing (Indexed Arrays)

SyntaxDescriptionExample
${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}

Modification

OperationSyntaxExample
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[@]}")
⚠️ Quote Array Expansions
Always use "${arr[@]}" with quotes to preserve elements with spaces. Without quotes, elements get word-split.

Detailed Examples

1
Array Declaration and Initialization

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
Output: === Indexed Arrays === Direct: apple banana cherry date Individual: red green blue Sparse: first sixth eleventh Sparse indices: 0 5 10 From ls: none From file: line one line two line three With indices: zero two four === Associative Arrays (Bash 4+) === User: name=John, age=30, city=New York Config: host=localhost, port=3306 Full name: John Doe === Array Properties === Array: one two three four five Length: 5 Indices: 0 1 2 3 4 First: one Last: five Element 2 length: 5 === Empty vs Unset === Empty array length: 0 Unset array length: 0 arr is defined empty has no elements
2
Iteration Methods

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
Output: === Iterate Over Elements === For-each: apple banana cherry date elderberry C-style loop: [0] = apple [1] = banana [2] = cherry [3] = date [4] = elderberry By index: arr[0] = apple arr[1] = banana ... === Iterate Associative Array === Key-value pairs: host = localhost port = 3306 user = admin pass = secret === Iterate in Reverse === Reverse order: E D C B A === Iterate Over Slices === Elements 2-4: c d e Last 3 elements: e f g
3
Adding and Removing Elements

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)"
Output: === Appending Elements === Initial: one two three After +=: one two three four After += multiple: one two three four five six After [10]=: one two three four five six ten Indices: 0 1 2 3 4 5 10 === Prepending Elements === Initial: two three four After prepend: one two three four After prepend multiple: minus-one zero one two three four === Inserting at Position === Initial: a b d e After insert at 2: a b c d e After insert_at: a a.5 b c d e === Removing Elements === Initial: a b c d e After unset [2]: a b d e Indices now: 0 1 3 4 After re-index: a b d e Indices now: 0 1 2 3 === Remove by Value === Initial: apple banana cherry banana date Remove first 'banana': apple cherry banana date Remove all 'banana': apple cherry date === Pop and Shift === Initial: first second third fourth fifth Popped 'fifth': first second third fourth Shifted 'first': second third fourth
⚠️ unset Leaves Gaps
Using unset 'arr[i]' removes the element but doesn't re-index. Use arr=("${arr[@]}") to re-index if needed.
4
Searching Arrays

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")"
Output: === Check If Element Exists === Array: apple banana cherry date elderberry banana 'cherry' found 'grape' not found 'banana' found (pattern match) === Find Index of Element === Index of 'cherry': 2 Index of 'grape': -1 === Find All Indices === All indices of 'banana': 1 5 === Count Occurrences === Count of 'banana': 2 Count of 'apple': 1 === Search with Pattern === Files: report.txt data.csv image.png notes.txt photo.jpg Text files (.txt): report.txt notes.txt === Search Associative Array === Admin users: alice charlie alice exists with role: admin === Binary Search (Sorted Array) === Sorted array: alpha beta delta gamma omega Binary search 'gamma': index 3 Binary search 'epsilon': index -1
5
Sorting Arrays

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[*]}"
Output: === Alphabetical Sort === Original: banana apple cherry date elderberry Sorted: apple banana cherry date elderberry Reversed: elderberry date cherry banana apple === Numerical Sort === Original: 10 5 3 8 1 9 2 7 4 6 Sorted: 1 2 3 4 5 6 7 8 9 10 Reversed: 10 9 8 7 6 5 4 3 2 1 === Case-Insensitive Sort === Original: Apple banana Cherry date Case-insensitive: Apple banana Cherry date === Sort by Field === Original: alice:30 bob:25 charlie:35 dave:28 By name: alice:30 bob:25 charlie:35 dave:28 By age: bob:25 dave:28 alice:30 charlie:35 === Version Sort === Original: 1.10.0 1.2.0 1.9.0 2.0.0 1.1.0 Version: 1.1.0 1.2.0 1.9.0 1.10.0 2.0.0 === Shuffle Array === Original: A B C D E Shuffled: C E A D B
6
Array Transformation

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
Output: === Map: Transform Each Element === Original: 1 2 3 4 5 Squared: 1 4 9 16 25 Doubled: 2 4 6 8 10 === Map Strings === Original: john jane bob Upper: JOHN JANE BOB Prefixed: user_john user_jane user_bob Suffixed: john_account jane_account bob_account === Filter: Select Elements === Original: 1 2 3 4 5 6 7 8 9 10 Evens: 2 4 6 8 10 GT 5: 6 7 8 9 10 === Reduce: Aggregate Values === Numbers: 1 2 3 4 5 Sum: 15 Product: 120 Min: 1 Max: 5 === Join/Flatten === Array: one two three Comma joined: one,two,three Pipe joined: one|two|three === Zip Two Arrays === Zipped: name = John age = 30 city = NYC
7
Passing Arrays to Functions

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
Output: === Method 1: Pass by Name Reference (Bash 4.3+) === Before: one two three In function: Length: 3 Elements: one two three After: one two three added === Method 2: Pass All Elements === Numbers: 10 20 30 40 50 Sum: 150 === Method 3: Return Array via Echo === Original: 1 2 3 4 5 Doubled: 2 4 6 8 10 === Method 4: Return via Global Variable === Numbers: 1 2 3 4 5 6 7 8 9 10 Evens: 2 4 6 8 10 === Associative Array by Reference === Configuration: debug = true host = localhost port = 8080 === Multiple Array Parameters === First: a b c Second: d e f Merged: a b c d e f === Practical: Array Processing Pipeline === Data: 1 2 3 4 5 6 7 8 9 10 Filtered (>5): 6 7 8 9 10 Squared: 36 49 64 81 100 Sum: 330
💡 Name References (Bash 4.3+)
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.
8
Associative Array Patterns

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
Output: === Basic Associative Array === User: active = true role = admin name = John Doe email = john@example.com === Check Key Existence === Key 'email' exists: = john@example.com Key 'phone' does not exist Key 'name' has value: John Doe === Default Values === Name: John Doe Phone: Not provided === Nested Structures (Simulated) === Simulated nested: user.name = John user.email = = john@example.com server.host = localhost server.port = 8080 === Count and Group === Word counts: apple: 3 banana: 2 cherry: 1 === Merge Associative Arrays === Merged config: timeout = 30 port = 5432 host = localhost database = myapp === Export to JSON-like Format === {"city": "NYC", "age": "30", "name": "John"}
9
Practical: Command Line Arguments

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
Output: === Basic Argument Array === All args: -v --config /etc/app.conf file1.txt file2.txt Arg count: 5 As array: -v --config /etc/app.conf file1.txt file2.txt First arg: -v Last arg: file2.txt === Parse Options and Arguments === Options: verbose: true config: /etc/app.conf output: out.txt Files: input1.txt input2.txt === getopt-style Short Options === Verbose: true Force: true Output: output.txt Inputs: config.txt input.txt === Build Command Array === Command: ls -la --color=auto -v /tmp /var Execute: total 28 drwxrwxrwt 1 root root 4096 Nov 19 15:30 . ... === Shift Through Arguments === Processing arguments: Current: first (remaining: 4) Current: second (remaining: 3) Current: third (remaining: 2) Current: fourth (remaining: 1)
💡 Safe Command Execution
Store commands in arrays and execute with "${cmd[@]}". This preserves arguments with spaces and prevents word splitting issues.
10
Complete Array Library

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 "========================================"
Output: ======================================== Array Library Demo ======================================== === Basic Operations === Array: one two three four five Length: 5 First: one Last: five === Search Operations === Contains 'three': yes Index of 'three': 2 === Modification === Initial: a b c After push d,e: a b c d e Popped 'e': a b c d Shifted 'a': b c d Unshift x,y: x y b c d === Transformation === Initial: c a b a c Unique: c a b Sorted: a b c Reversed: c b a === Aggregation === Numbers: 5 2 8 1 9 3 Sum: 28 Min: 1 Max: 9 === Join === Comma: one,two,three Pipe: one | two | three ========================================

Quick Reference

Essential Syntax

# 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

Common Patterns

TaskCode
Check if empty[[ ${#arr[@]} -eq 0 ]]
Copy arraynew=("${old[@]}")
Merge arraysall=("${a[@]}" "${b[@]}")
Read file to arrayreadarray -t arr < file
Sort arrayreadarray -t arr < <(printf '%s\n' "${arr[@]}" | sort)
Join with comma$(IFS=','; echo "${arr[*]}")
Check contains[[ " ${arr[*]} " =~ " value " ]]
Check key exists[[ -v arr[key] ]]
✓ Best Practices
✗ Common Mistakes