How to Do Nested Loops
Quick Answer: Create Nested Loops in Bash
Nested loops are loops inside other loops. Use the same loop syntax (for, while, etc.) and nest one inside the other. For each iteration of the outer loop, the inner loop runs completely. Example: for i in 1 2 3; do for j in a b c; do echo "$i-$j"; done; done.
Quick Comparison: Loop Nesting Strategies
| Approach | Best For | Complexity | Performance |
|---|---|---|---|
| Numeric nested | Counters, ranges | Simple | Very fast |
| Array nested | Multiple data sets | Moderate | Fast |
| File/directory | Processing files | Moderate | Medium |
| Three-level nest | Complex data | Complex | Slower |
| Break/continue | Conditional exit | Moderate | Fast |
Bottom line: Keep nesting to 2 levels for readability. Use unique variable names (i, j, k) and test with small data first.
What Are Nested Loops?
Nested loops are simply loops inside other loops. When you have a loop within a loop, the inner loop completes all its iterations for each iteration of the outer loop. This is incredibly useful for processing multi-dimensional data, working with multiple files or directories, or generating combinations.
For example, if you have an outer loop that runs 3 times and an inner loop that runs 4 times, the total iterations will be 3 × 4 = 12.
Basic Nested Loop Structure
Here’s the most basic nested loop structure:
#!/bin/bash
for ((i=1; i<=3; i++)); do
echo "Outer loop iteration $i"
for ((j=1; j<=3; j++)); do
echo " Inner loop iteration $j"
done
done
Output:
Outer loop iteration 1
Inner loop iteration 1
Inner loop iteration 2
Inner loop iteration 3
Outer loop iteration 2
Inner loop iteration 1
Inner loop iteration 2
Inner loop iteration 3
Outer loop iteration 3
Inner loop iteration 1
Inner loop iteration 2
Inner loop iteration 3
Notice how the inner loop completes all iterations (1, 2, 3) before the outer loop moves to its next iteration.
When to Use Basic Nested Loops
- You’re iterating through ranges or sequences
- You need numeric indices or counters
- You’re generating combinations of numbers
- You want simple, readable code
Practical Examples
Creating a Multiplication Table
A classic use of nested loops is generating a multiplication table:
#!/bin/bash
echo "Multiplication Table"
echo ""
for ((i=1; i<=5; i++)); do
for ((j=1; j<=5; j++)); do
result=$((i * j))
printf "%3d " $result
done
echo # Newline after each row
done
Output:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
When to Use Numeric Nested Loops
- You need precise control over iterations
- You’re generating tables or matrices
- You’re doing mathematical calculations
- You need formatted output
Processing Directories and Files
A practical nested loop example - process multiple directories and files:
#!/bin/bash
# List of directories to process
DIRS=("/home/user/docs" "/home/user/downloads" "/home/user/desktop")
# For each directory
for dir in "${DIRS[@]}"; do
if [ -d "$dir" ]; then
echo "Processing directory: $dir"
# Count files (simple example)
count=0
for file in "$dir"/*; do
if [ -f "$file" ]; then
((count++))
fi
done
echo " Found $count files"
fi
done
When to Use File/Directory Nested Loops
- You’re processing multiple directories
- You need to work with files in different locations
- You want to count or filter files
- You’re building file processing scripts
Nested Loops with Arrays
Working with multiple arrays:
#!/bin/bash
users=("alice" "bob" "charlie")
actions=("read" "write" "execute")
echo "Permission Matrix:"
for user in "${users[@]}"; do
echo -n "$user: "
for action in "${actions[@]}"; do
echo -n "[$action] "
done
echo # Newline
done
Output:
Permission Matrix:
alice: [read] [write] [execute]
bob: [read] [write] [execute]
charlie: [read] [write] [execute]
When to Use Array Nested Loops
- You have multiple arrays to iterate through
- You’re creating permission or access matrices
- You’re combining two sets of data
- You need cartesian products
Real-World Example: Log File Analysis
Process multiple log files with nested loops:
#!/bin/bash
LOG_DIRS=("/var/log/app" "/var/log/system")
ERROR_KEYWORDS=("ERROR" "CRITICAL" "FAILED")
for log_dir in "${LOG_DIRS[@]}"; do
echo "Analyzing logs in: $log_dir"
# Process each log file
for log_file in "$log_dir"/*.log; do
if [ -f "$log_file" ]; then
echo " File: $(basename "$log_file")"
# Check for each error keyword
for keyword in "${ERROR_KEYWORDS[@]}"; do
count=$(grep -c "$keyword" "$log_file" 2>/dev/null || echo 0)
if (( count > 0 )); then
echo " $keyword: $count occurrences"
fi
done
fi
done
done
When to Use Log File Analysis Pattern
- You’re analyzing multiple logs
- You need to search for multiple keywords
- You want aggregated results
- You’re building monitoring scripts
Nested Loops with Break and Continue
Control flow in nested loops:
#!/bin/bash
for ((i=1; i<=3; i++)); do
echo "Outer loop: $i"
for ((j=1; j<=5; j++)); do
if (( j == 3 )); then
echo " Skipping iteration 3"
continue # Skip to next inner loop iteration
fi
if (( j == 4 )); then
echo " Breaking inner loop at iteration 4"
break # Exit inner loop
fi
echo " Inner: $j"
done
echo "Outer loop $i complete"
done
Output:
Outer loop: 1
Inner: 1
Inner: 2
Skipping iteration 3
Breaking inner loop at iteration 4
Outer loop 1 complete
Outer loop: 2
Inner: 1
Inner: 2
Skipping iteration 3
Breaking inner loop at iteration 4
Outer loop 2 complete
Outer loop: 3
Inner: 1
Inner: 2
Skipping iteration 3
Breaking inner loop at iteration 4
Outer loop 3 complete
When to Use Break and Continue
- You need to skip iterations conditionally
- You want to exit inner loops early
- You’re implementing complex logic
- You’re handling error conditions
Three-Level Nesting
Sometimes you need three levels:
#!/bin/bash
# Organize data in three dimensions
for ((year=2024; year<=2025; year++)); do
echo "Year: $year"
for ((month=1; month<=3; month++)); do
echo " Month: $month"
for ((week=1; week<=4; week++)); do
echo " Week: $week"
done
done
done
When to Use Three-Level Nesting
- You’re organizing data in 3 dimensions (year/month/week)
- You have three independent iteration variables
- You’re processing complex hierarchical data
- Note: Deeper nesting often indicates code needs refactoring
Processing File Names
Nested loops with string manipulation:
#!/bin/bash
SOURCE_DIR="/home/user/files"
EXTENSIONS=("txt" "log" "bak")
# For each extension
for ext in "${EXTENSIONS[@]}"; do
echo "Processing .$ext files"
# Find and process each file
count=0
for file in "$SOURCE_DIR"/*."$ext"; do
if [ -f "$file" ]; then
((count++))
echo " Found: $(basename "$file")"
fi
done
echo " Total .$ext files: $count"
done
Batch Processing Multiple File Types
Nested loops are useful for processing different file types across directories.
Performance Considerations
Nested loops can be slow with large datasets. Here are some optimization tips:
#!/bin/bash
# Slow: Multiple subshells, slow grep calls
for file in /var/log/*; do
for line in $(grep "ERROR" "$file"); do
echo "$line"
done
done
# Better: Single grep call, no subshells
for file in /var/log/*; do
grep "ERROR" "$file"
done
# Best: Use find with exec or awk for complex operations
find /var/log -type f -exec grep "ERROR" {} \;
Quick Reference
# Basic nested loop
for ((i=1; i<=3; i++)); do
for ((j=1; j<=3; j++)); do
echo "$i,$j"
done
done
# Nested array loops
for item1 in "${arr1[@]}"; do
for item2 in "${arr2[@]}"; do
echo "$item1-$item2"
done
done
# With break/continue
for ((i=1; i<=3; i++)); do
for ((j=1; j<=3; j++)); do
if (( j == 2 )); then continue; fi
echo "$i,$j"
done
done
Debugging Nested Loops
When nested loops don’t work as expected, add debugging output:
#!/bin/bash
# Set debug mode
DEBUG=1
for ((i=1; i<=2; i++)); do
[[ $DEBUG -eq 1 ]] && echo "DEBUG: Outer i=$i"
for ((j=1; j<=2; j++)); do
[[ $DEBUG -eq 1 ]] && echo "DEBUG: Inner j=$j"
echo " Processing: $i,$j"
done
done
Or use bash’s built-in debugging:
# Run with debugging enabled
bash -x script.sh
# Or enable debugging in the script
set -x
# ... your nested loops ...
set +x
Important Notes
- Indentation matters for readability - use consistent spacing
- Variable scope - inner loops can use outer loop variables
- Performance - too many nested levels can slow your script
- Break affects only the inner loop - to break outer loop, use labels or flags
- Avoid deep nesting - 3+ levels usually indicates refactoring is needed
Common Mistakes
Forgetting variable names:
# WRONG - i is reused
for i in 1 2 3; do
for i in a b c; do # This shadows outer i
echo $i
done
done
# RIGHT
for i in 1 2 3; do
for j in a b c; do
echo $i,$j
done
done
Summary
Nested loops are powerful for processing complex data structures. They’re commonly used for multi-dimensional arrays, file processing, and generating combinations. Keep them readable by using unique variable names (i, j, k), avoid nesting more than 2-3 levels deep, and remember that the inner loop completes all iterations before the outer loop advances. Test with small datasets before running on production data.