Skip to main content

How to Do Nested Loops

• 3 min read
bash

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

ApproachBest ForComplexityPerformance
Numeric nestedCounters, rangesSimpleVery fast
Array nestedMultiple data setsModerateFast
File/directoryProcessing filesModerateMedium
Three-level nestComplex dataComplexSlower
Break/continueConditional exitModerateFast

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.