advanced-array-patterns

安装量: 52
排名: #14180

安装

npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill advanced-array-patterns

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

Method 1: Direct assignment

files=("file1.txt" "file2.txt" "file with spaces.txt")

Method 2: Compound assignment

declare -a numbers=(1 2 3 4 5)

Method 3: Individual assignment

fruits[0]="apple" fruits[1]="banana" fruits[2]="cherry"

Method 4: From command output (CAREFUL with word splitting)

✗ DANGEROUS - splits on spaces

files_bad=$(ls)

✓ SAFE - preserves filenames with spaces

mapfile -t files_good < <(find . -name "*.txt")

Method 5: Brace expansion

numbers=({1..100}) letters=({a..z})

Array Operations

!/usr/bin/env bash

set -euo pipefail

arr=("first" "second" "third" "fourth" "fifth")

Length

echo "Length: ${#arr[@]}" # 5

Access elements

echo "First: ${arr[0]}" echo "Last: ${arr[-1]}" # Bash 4.3+ echo "Second to last: ${arr[-2]}"

All elements (properly quoted for spaces)

for item in "${arr[@]}"; do echo "Item: $item" done

All indices

for idx in "${!arr[@]}"; do echo "Index $idx: ${arr[$idx]}" done

Slice (offset:length)

echo "${arr[@]:1:3}" # second third fourth

Slice from offset to end

echo "${arr[@]:2}" # third fourth fifth

Append element

arr+=("sixth")

Insert at position (complex)

arr=("${arr[@]:0:2}" "inserted" "${arr[@]:2}")

Remove element by index

unset 'arr[2]'

Remove by value (all occurrences)

arr_new=() for item in "${arr[@]}"; do [[ "$item" != "second" ]] && arr_new+=("$item") done arr=("${arr_new[@]}")

Check if empty

if [[ ${#arr[@]} -eq 0 ]]; then echo "Array is empty" fi

Check if element exists

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")

Map (transform each element)

upper_arr=() for item in "${arr[@]}"; do upper_arr+=("${item^^}") # Uppercase done

Filter

filtered=() for item in "${arr[@]}"; do [[ ${#item} -gt 5 ]] && filtered+=("$item") done

Join array to string

IFS=',' joined="${arr[*]}" unset IFS echo "$joined" # apple,banana,cherry,date

Split string to array

IFS=',' read -ra split_arr <<< "one,two,three"

Unique values

declare -A seen unique=() for item in "${arr[@]}"; do if [[ -z "${seen[$item]:-}" ]]; then seen[$item]=1 unique+=("$item") fi done

Sort array

readarray -t sorted < <(printf '%s\n' "${arr[@]}" | sort)

Reverse array

reversed=() for ((i=${#arr[@]}-1; i>=0; i--)); do reversed+=("${arr[$i]}") done

Or using tac

readarray -t reversed < <(printf '%s\n' "${arr[@]}" | tac)

Associative Arrays (Bash 4+) Declaration and Usage

!/usr/bin/env bash

set -euo pipefail

MUST declare with -A

declare -A config

Assignment

config["host"]="localhost" config["port"]="8080" config["debug"]="true"

Or compound assignment

declare -A user=( [name]="John Doe" [email]="john@example.com" [role]="admin" )

Access

echo "Host: ${config[host]}" echo "User: ${user[name]}"

Default value if key missing

echo "${config[missing]:-default}"

Check if key exists

if [[ -v config[host] ]]; then echo "Host is set" fi

Alternative check

if [[ -n "${config[host]+x}" ]]; then echo "Host key exists (even if empty)" fi

All keys

echo "Keys: ${!config[@]}"

All values

echo "Values: ${config[@]}"

Length (number of keys)

echo "Size: ${#config[@]}"

Iterate

for key in "${!config[@]}"; do echo "$key = ${config[$key]}" done

Delete key

unset 'config[debug]'

Clear entire array

config=()

Real-World Associative Array Patterns

!/usr/bin/env bash

set -euo pipefail

Configuration parser

parse_config() { local config_file="$1" declare -gA CONFIG # Global associative array

while IFS='=' read -r key value; do
    # Skip comments and empty lines
    [[ "$key" =~ ^[[:space:]]*# ]] && continue
    [[ -z "$key" ]] && continue

    # Trim whitespace
    key="${key//[[:space:]]/}"
    value="${value#"${value%%[![:space:]]*}"}"  # Left trim
    value="${value%"${value##*[![:space:]]}"}"  # Right trim

    CONFIG["$key"]="$value"
done < "$config_file"

}

Usage

parse_config "/etc/myapp.conf" echo "Database: ${CONFIG[database]:-not set}"

Counter/frequency map

count_words() { local file="$1" declare -A word_count

while read -ra words; do
    for word in "${words[@]}"; do
        # Normalize: lowercase, remove punctuation
        word="${word,,}"
        word="${word//[^a-z]/}"
        [[ -n "$word" ]] && ((word_count[$word]++))
    done
done < "$file"

# Print sorted by count
for word in "${!word_count[@]}"; do
    echo "${word_count[$word]} $word"
done | sort -rn | head -10

}

Caching pattern

declare -A CACHE

cached_expensive_operation() { local key="$1"

# Check cache
if [[ -n "${CACHE[$key]+x}" ]]; then
    echo "${CACHE[$key]}"
    return 0
fi

# Compute and cache
local result
result=$(expensive_computation "$key")
CACHE["$key"]="$result"
echo "$result"

}

JSON-like nested data (using delimited keys)

declare -A data data["user.name"]="John" data["user.email"]="john@example.com" data["user.address.city"]="NYC" data["user.address.zip"]="10001"

Access nested

echo "City: ${data[user.address.city]}"

mapfile / readarray (Bash 4+) Basic Usage

!/usr/bin/env bash

set -euo pipefail

Read file into array (each line = element)

mapfile -t lines < file.txt

Or equivalently:

readarray -t lines < file.txt

-t removes trailing newlines

Without -t, each element includes \n

Process each line

for line in "${lines[@]}"; do echo "Line: $line" done

Read from command output

mapfile -t files < <(find . -name "*.sh")

Read from here-doc

mapfile -t data <<'EOF' line1 line2 line3 EOF

Advanced mapfile Options

!/usr/bin/env bash

set -euo pipefail

-n COUNT: Read at most COUNT lines

mapfile -t -n 10 first_10 < large_file.txt

-s COUNT: Skip first COUNT lines

mapfile -t -s 1 skip_header < data.csv # Skip header row

-O INDEX: Start at INDEX instead of 0

existing_array=("a" "b") mapfile -t -O "${#existing_array[@]}" existing_array < more_data.txt

-d DELIM: Use DELIM instead of newline (Bash 4.4+)

Read NUL-delimited data (safe for filenames with newlines)

mapfile -t -d '' files < <(find . -name "*.txt" -print0)

-C CALLBACK: Execute callback every QUANTUM lines

-c QUANTUM: Number of lines between callbacks (default 5000)

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 file

parse_csv() { local csv_file="$1" local -n result_array="$2" # nameref (Bash 4.3+)

while IFS=',' read -ra row; do
    result_array+=("${row[*]}")  # Store as delimited string
done < "$csv_file"

}

Better: Store as 2D array simulation

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

Access cell

echo "Row 2, Col 3: ${csv_data[2,3]}"

Performance Patterns Efficient Array Building

!/usr/bin/env bash

set -euo pipefail

✗ SLOW - Command substitution in loop

slow_build() { local arr=() for i in {1..1000}; do arr+=("$(echo "$i")") # Subshell for each! done }

✓ FAST - Direct assignment

fast_build() { local arr=() for i in {1..1000}; do arr+=("$i") # No subshell done }

✓ FASTEST - mapfile for file data

fastest_file_read() { mapfile -t arr < file.txt }

Avoid Subshells in Loops

!/usr/bin/env bash

set -euo pipefail

✗ SLOW - Subshell each iteration

slow_process() { local sum=0 for num in "${numbers[@]}"; do result=$(echo "$num * 2" | bc) # Subshell! ((sum += result)) done }

✓ FAST - Bash arithmetic

fast_process() { local sum=0 for num in "${numbers[@]}"; do ((sum += num * 2)) done }

✓ FAST - Process substitution for parallel reads

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

For very large arrays, consider:

1. Process in chunks

2. Use external tools (awk, sort)

3. Stream processing instead of loading all

Chunk processing

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

}

Stream processing (memory efficient)

Instead of:

mapfile -t all_lines < huge_file.txt

process "${all_lines[@]}"

Use:

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

Requires Bash 5.2+

set -euo pipefail

declare -A config

Subscript expressions evaluated once (5.2+)

key="host" config[$key]="localhost" # Evaluated correctly

'@' and '*' subscripts for associative arrays

Can now unset just the key '@' instead of entire array

declare -A special special[@]="at sign value" special[*]="asterisk value" special[normal]="normal value"

Unset specific key (Bash 5.2+)

unset 'special[@]' # Only removes '@' key, not whole array

GLOBSORT with Arrays

!/usr/bin/env bash

Requires Bash 5.3

set -euo pipefail

Sort glob results by modification time (newest first)

GLOBSORT="-mtime" recent_files=(*.txt)

Sort by size

GLOBSORT="size" files_by_size=(*.log)

Reset to default (alphabetical)

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 }

Usage

push "first" push "second" push "third" echo "Top: $(peek)" # third echo "Pop: $(pop)" # third echo "Pop: $(pop)" # second

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}") }

Usage

enqueue "task1" enqueue "task2" enqueue "task3" echo "Next: $(dequeue)" # task1 echo "Next: $(dequeue)" # task2

Set Operations

!/usr/bin/env bash

set -euo pipefail

Union

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[@]}"

}

Intersection

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[@]}"

}

Difference (arr1 - arr2)

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 Bash Arrays BashFAQ/005 - Arrays Bash Hackers - Arrays

Master bash arrays for efficient data manipulation and avoid common pitfalls like word splitting and subshell overhead.

返回排行榜