14.1. Arrays#
14.1.1. Advanced Array Patterns#
14.1.1.1. Array Functions#
#!/bin/bash
# Check if element in array
array_contains() {
local target=$1
shift
local -a arr=("$@")
for item in "${arr[@]}"; do
[[ "$item" == "$target" ]] && return 0
done
return 1
}
if array_contains "cherry" apple banana cherry date; then
echo "Found cherry"
fi
# Remove element from array
array_remove() {
local target=$1
shift
local -a arr=("$@")
local -a result=()
for item in "${arr[@]}"; do
[[ "$item" != "$target" ]] && result+=("$item")
done
echo "${result[@]}"
}
new_arr=($(array_remove "banana" apple banana cherry))
# Sort array
array_sort() {
local -a arr=("$@")
printf '%s\n' "${arr[@]}" | sort
}
sorted=($(array_sort 3 1 4 1 5 9 2 6))
# Unique elements
array_unique() {
local -a arr=("$@")
printf '%s\n' "${arr[@]}" | sort -u
}
unique=($(array_unique 3 1 4 1 5 9 2 6))
14.1.1.2. Working with Multidimensional Data#
#!/bin/bash
# Simulate 2D array (using special delimiter)
declare -A matrix
set_cell() {
local row=$1 col=$2 value=$3
matrix["$row:$col"]=$value
}
get_cell() {
local row=$1 col=$2
echo ${matrix["$row:$col"]}
}
# Create 3x3 grid
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
set_cell $i $j "[$i,$j]"
done
done
# Print matrix
print_matrix() {
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
printf "%8s " "$(get_cell $i $j)"
done
echo
done
}
print_matrix
# List data (array of associative arrays)
declare -a users=(
"[name]=Alice [age]=25 [city]=NYC"
"[name]=Bob [age]=30 [city]=LA"
"[name]=Charlie [age]=35 [city]=SF"
)
# Parse and use
for user_str in "${users[@]}"; do
declare -A user
eval "$user_str"
echo "${user[name]} is ${user[age]} years old from ${user[city]}"
unset user
done
14.1.2. Associative Arrays (Hash Maps)#
Associative arrays map keys to values, enabling sophisticated data structures.
14.1.2.1. Basic Associative Arrays#
#!/bin/bash
# Declare associative array
declare -A config
config[host]="localhost"
config[port]=8080
config[debug]="true"
# Initialize with values
declare -A user=(
[name]="John"
[age]=30
[email]="john@example.com"
)
# Add elements
config[timeout]=5000
# Access elements
echo ${config[host]} # localhost
echo ${config[port]} # 8080
# All keys
echo ${!config[@]} # host port debug timeout
# All values
echo ${config[@]} # localhost 8080 true 5000
# Check if key exists
if [[ -v config[host] ]]; then
echo "host key exists"
fi
14.1.2.2. Iterating Associative Arrays#
#!/bin/bash
declare -A servers=(
[web1]="192.168.1.10"
[web2]="192.168.1.11"
[db1]="192.168.1.20"
[db2]="192.168.1.21"
)
# Iterate over keys
for server in "${!servers[@]}"; do
echo "$server: ${servers[$server]}"
done
# Iterate with sort (keys)
for server in $(echo "${!servers[@]}" | tr ' ' '\n' | sort); do
echo "$server: ${servers[$server]}"
done
# Iterate over values
for ip in "${servers[@]}"; do
ping -c 1 "$ip"
done
# Find key by value
find_key_by_value() {
local target=$1
for key in "${!servers[@]}"; do
if [[ "${servers[$key]}" == "$target" ]]; then
echo "$key"
return 0
fi
done
return 1
}
server_name=$(find_key_by_value "192.168.1.10")
echo "IP 192.168.1.10 belongs to: $server_name"
14.1.2.3. Practical Use Cases#
#!/bin/bash
# Configuration management
declare -A db_config=(
[host]="localhost"
[port]="5432"
[user]="postgres"
[password]="secret"
)
# Function to get config with default
get_config() {
local key=$1
local default=${2:-""}
if [[ -v db_config[$key] ]]; then
echo "${db_config[$key]}"
else
echo "$default"
fi
}
# Count occurrences
count_items() {
local -a items=("$@")
declare -A counts
for item in "${items[@]}"; do
((counts[$item]++))
done
for item in "${!counts[@]}"; do
echo "$item: ${counts[$item]}"
done
}
count_items apple banana apple cherry banana apple
# Output:
# apple: 3
# banana: 2
# cherry: 1
# Group by attribute
declare -A groups
while IFS=":" read -r name group; do
[[ "$name" == "#"* ]] && continue # Skip comments
groups[$group]+="$name "
done < /etc/group
# Print groups
for group_name in "${!groups[@]}"; do
echo "$group_name: ${groups[$group_name]}"
done
14.1.2.4. Common Pitfalls#
#!/bin/bash
# Pitfall 1: Not declaring as associative array
bad_array[key]="value" # Treats as indexed array, key becomes 0
declare -A good_array
good_array[key]="value" # Correctly uses key
# Pitfall 2: Forgetting quotes with @ or *
declare -A arr=(["a"]="1" ["b"]="2")
for key in ${!arr[@]}; do # WRONG: expands to "a b"
echo "$key"
done
# Should be: for key in "${!arr[@]}"; do
# Pitfall 3: Keys with spaces
config["log level"]="debug" # Works, but key has space
config["log_level"]="debug" # Better: use underscore
# Pitfall 4: Unset elements
unset arr[key]
echo ${arr[key]} # Empty, but no error
# Check with -v
if [[ -v arr[key] ]]; then
echo "Key exists"
else
echo "Key doesn't exist"
fi
14.1.3. Working with Indexed Arrays#
Indexed arrays store multiple values with numeric indices, fundamental for data processing.
14.1.3.1. Array Basics#
#!/bin/bash
# Create array with explicit indices
declare -a fruits=("apple" "banana" "cherry")
# Implicit indices (0-based)
colors=(red green blue yellow)
# Add to array
colors+=(orange purple) # Append multiple
# Array with gaps in indices
declare -a sparse
sparse[0]="first"
sparse[5]="sixth" # Creates sparse array
sparse[2]="third"
# Create array from command output
files=($(ls *.txt))
lines=($(cat file.txt))
# Create from variable expansion
numbers=({1..10}) # Brace expansion
letters=(a b c d e)
14.1.3.2. Accessing Array Elements#
#!/bin/bash
arr=(apple banana cherry date elderberry)
# Access by index
echo ${arr[0]} # apple
echo ${arr[2]} # cherry
echo ${arr[-1]} # elderberry (Bash 4.3+, negative indexing)
# All elements
echo ${arr[@]} # apple banana cherry date elderberry
echo ${arr[*]} # Same, but as single word if unquoted
# Length of array
echo ${#arr[@]} # 5
echo ${#arr[*]} # 5
# Indices
echo ${!arr[@]} # 0 1 2 3 4
# Substring of element
echo ${arr[0]:0:3} # app
echo ${arr[0]:1:3} # ppl
# Array slice (Bash 4.3+)
echo "${arr[@]:1:3}" # banana cherry date
14.1.3.3. Iterating Arrays#
#!/bin/bash
arr=(apple banana cherry)
# Loop with index
for i in "${!arr[@]}"; do
echo "$i: ${arr[$i]}"
done
# Output:
# 0: apple
# 1: banana
# 2: cherry
# Loop over elements
for fruit in "${arr[@]}"; do
echo "Fruit: $fruit"
done
# While loop with index
i=0
while [[ $i -lt ${#arr[@]} ]]; do
echo "${arr[$i]}"
((i++))
done
# Conditional iteration
for item in "${arr[@]}"; do
if [[ "$item" == "banana" ]]; then
echo "Found banana at index $i"
fi
done
14.1.3.4. Array Operations#
#!/bin/bash
arr=(3 1 4 1 5 9 2 6)
# Remove element
unset arr[2] # Removes element 4
# Remove entire array
unset arr
# Remove suffix from each element
declare -a paths=("/home/user" "/home/admin" "/var/log")
paths=("${paths[@]%/*}") # Remove after last /
# Result: /home /home /var
# Replace in array
arr=(apple banana cherry)
arr=("${arr[@]/banana/grape}") # Replace banana with grape
# Result: apple grape cherry
# Convert to string
IFS=","
string="${arr[*]}" # Join with comma
IFS=$'\n'
# Create array from string
IFS="," read -ra arr <<< "apple,banana,cherry"
# Result: arr=(apple banana cherry)