Bash Arrays: A Comprehensive Guide with Examples
Arrays are powerful data structures in Bash that allow you to store multiple values in a single variable. This comprehensive guide will teach you everything about Bash arrays! π
Introduction to Bash Arrays
Bash supports two types of arrays:
- Indexed Arrays: Elements accessed by numeric index (0-based)
- Associative Arrays: Elements accessed by string keys (Bash 4.0+)
Indexed Arrays
Declaring Indexed Arrays
# Method 1: Declare and initialize
fruits=("apple" "banana" "orange" "grape")
# Method 2: Declare empty array
declare -a colors
# Method 3: Assign individual elements
animals[0]="cat"
animals[1]="dog"
animals[2]="bird"
# Method 4: Read from command output
files=($(ls *.txt))
# Method 5: Using declare
declare -a numbers=(1 2 3 4 5)
Accessing Array Elements
# Access single element
echo ${fruits[0]} # apple
echo ${fruits[2]} # orange
# Access all elements
echo ${fruits[@]} # apple banana orange grape
echo ${fruits[*]} # apple banana orange grape
# Get array length
echo ${#fruits[@]} # 4
# Get length of specific element
echo ${#fruits[1]} # 6 (length of "banana")
Array Operations
# Adding elements
fruits+=("watermelon")
fruits[5]="mango"
# Modifying elements
fruits[1]="strawberry"
# Deleting elements
unset fruits[2]
# Array slicing
echo ${fruits[@]:1:3} # Elements from index 1, next 3 elements
# Copy array
new_fruits=("${fruits[@]}")
Associative Arrays
Declaring Associative Arrays
# Declare associative array
declare -A user_info
# Initialize with values
user_info=(
["name"]="John Doe"
["email"]="[email protected]"
["age"]="30"
)
# Add elements individually
declare -A server_config
server_config["hostname"]="webserver01"
server_config["ip"]="192.168.1.100"
server_config["port"]="8080"
Working with Associative Arrays
# Access elements
echo ${user_info["name"]} # John Doe
# Get all keys
echo ${!user_info[@]} # name email age
# Get all values
echo ${user_info[@]} # John Doe [email protected] 30
# Check if key exists
if [[ -v user_info["email"] ]]; then
echo "Email exists: ${user_info["email"]}"
fi
Advanced Array Techniques
Looping Through Arrays
# Loop through indexed array
echo "Fruits in basket:"
for fruit in "${fruits[@]}"; do
echo "- $fruit"
done
# Loop with index
echo "Fruits with index:"
for i in "${!fruits[@]}"; do
echo "$i: ${fruits[$i]}"
done
# Loop through associative array
echo "User Information:"
for key in "${!user_info[@]}"; do
echo "$key: ${user_info[$key]}"
done
Array Functions
#!/bin/bash
# Function to print array
print_array() {
local -n arr=$1
echo "Array contents:"
for element in "${arr[@]}"; do
echo " - $element"
done
}
# Function to find element in array
array_contains() {
local -n arr=$1
local search=$2
for element in "${arr[@]}"; do
if [[ "$element" == "$search" ]]; then
return 0
fi
done
return 1
}
# Usage
my_array=("red" "green" "blue")
print_array my_array
if array_contains my_array "green"; then
echo "Found green in array"
fi
Array Manipulation
# Reverse an array
reverse_array() {
local -n arr=$1
local -a reversed=()
for ((i=${#arr[@]}-1; i>=0; i--)); do
reversed+=("${arr[$i]}")
done
arr=("${reversed[@]}")
}
# Sort array
sort_array() {
local -n arr=$1
IFS=$'\n' arr=($(sort <<<"${arr[*]}"))
unset IFS
}
# Remove duplicates
unique_array() {
local -n arr=$1
local -A seen=()
local -a unique=()
for element in "${arr[@]}"; do
if [[ ! -v seen["$element"] ]]; then
seen["$element"]=1
unique+=("$element")
fi
done
arr=("${unique[@]}")
}
Practical Examples
Example 1: File Processing
#!/bin/bash
# Process files in directory
declare -a text_files
declare -A file_info
# Find all text files
text_files=($(find . -name "*.txt" -type f))
echo "Found ${#text_files[@]} text files"
# Gather file information
for file in "${text_files[@]}"; do
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file")
lines=$(wc -l < "$file")
file_info["$file,size"]=$size
file_info["$file,lines"]=$lines
done
# Display file information
for file in "${text_files[@]}"; do
echo "File: $file"
echo " Size: ${file_info["$file,size"]} bytes"
echo " Lines: ${file_info["$file,lines"]}"
done
Example 2: Configuration Parser
#!/bin/bash
# Parse configuration file into associative array
declare -A config
parse_config() {
local config_file=$1
while IFS='=' read -r key value; do
# Skip comments and empty lines
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "$key" ]] && continue
# Trim whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
config["$key"]="$value"
done < "$config_file"
}
# Usage
parse_config "/etc/myapp.conf"
# Access configuration
echo "Database host: ${config[db_host]}"
echo "Database port: ${config[db_port]}"
Example 3: Menu System
#!/bin/bash
# Create menu using arrays
declare -a menu_options=(
"System Information"
"Disk Usage"
"Network Status"
"Process List"
"Exit"
)
declare -A menu_actions=(
["System Information"]="uname -a"
["Disk Usage"]="df -h"
["Network Status"]="ip addr show"
["Process List"]="ps aux | head -20"
["Exit"]="exit 0"
)
show_menu() {
echo "===== System Admin Menu ====="
for i in "${!menu_options[@]}"; do
echo "$((i+1)). ${menu_options[$i]}"
done
echo "=========================="
}
# Main loop
while true; do
show_menu
read -p "Select option: " choice
if [[ $choice -ge 1 && $choice -le ${#menu_options[@]} ]]; then
selected="${menu_options[$((choice-1))]}"
echo -e "\nExecuting: $selected\n"
eval "${menu_actions[$selected]}"
echo -e "\nPress Enter to continue..."
read
else
echo "Invalid option"
fi
done
Example 4: Data Processing
#!/bin/bash
# Process CSV data with arrays
declare -a headers
declare -a data_rows
read_csv() {
local csv_file=$1
local line_num=0
while IFS=',' read -r -a row; do
if [[ $line_num -eq 0 ]]; then
headers=("${row[@]}")
else
data_rows+=("${row[*]}")
fi
((line_num++))
done < "$csv_file"
}
# Calculate column statistics
calculate_stats() {
local column_idx=$1
local -a values=()
for row in "${data_rows[@]}"; do
IFS=',' read -r -a fields <<< "$row"
values+=("${fields[$column_idx]}")
done
# Calculate sum (assuming numeric values)
local sum=0
for val in "${values[@]}"; do
sum=$((sum + val))
done
echo "Column: ${headers[$column_idx]}"
echo "Sum: $sum"
echo "Count: ${#values[@]}"
echo "Average: $((sum / ${#values[@]}))"
}
Array Best Practices
1. Quote Array Expansions
# Good - preserves spaces and special characters
for item in "${array[@]}"; do
echo "$item"
done
# Bad - word splitting issues
for item in ${array[@]}; do
echo $item
done
2. Check Array Bounds
# Safe array access
safe_get() {
local -n arr=$1
local index=$2
if [[ $index -ge 0 && $index -lt ${#arr[@]} ]]; then
echo "${arr[$index]}"
else
echo "Index out of bounds" >&2
return 1
fi
}
3. Use Local Arrays in Functions
process_data() {
local -a temp_array=()
local -A cache=()
# Process data locally
temp_array+=("processed")
}
4. Array Serialization
# Save array to file
save_array() {
local -n arr=$1
local file=$2
printf '%s\n' "${arr[@]}" > "$file"
}
# Load array from file
load_array() {
local -n arr=$1
local file=$2
mapfile -t arr < "$file"
}
Common Pitfalls and Solutions
Pitfall 1: Sparse Arrays
# Creating sparse array
arr[5]="five"
arr[10]="ten"
# Problem: length is not what you expect
echo ${#arr[@]} # 2, not 11
# Solution: Use continuous indices or associative arrays
Pitfall 2: Word Splitting
# Problem
files="file 1.txt file 2.txt"
arr=($files) # Creates 4 elements!
# Solution
IFS=$'\n' arr=($(ls)) # Split on newlines
# Or use mapfile
mapfile -t arr < <(ls)
Pitfall 3: Passing Arrays to Functions
# Problem - only first element passed
my_func "${array[@]}"
# Solution - use nameref
my_func() {
local -n arr=$1
# Now arr references the original array
}
my_func array
Performance Tips
- Preallocate Size: For large arrays, consider preallocating
- Use Builtin Commands: Prefer bash builtins over external commands
- Avoid Repeated Lookups: Cache array lengths and frequently accessed elements
- Use Associative Arrays: For key-based lookups instead of searching
Conclusion
Bash arrays are versatile tools for script development. Whether youβre processing files, managing configurations, or building interactive scripts, arrays provide the flexibility you need.
Key takeaways:
- Use indexed arrays for ordered data
- Use associative arrays for key-value pairs
- Always quote array expansions
- Consider performance for large datasets
- Test edge cases with empty and sparse arrays
Master these array techniques to write more efficient and maintainable Bash scripts! π―