Web Analytics

Working with Directories

Intermediate ~25 min read

Directories are the backbone of file organization. This lesson teaches you to navigate, iterate, and process directory contents efficiently. You'll master multiple approaches from simple globs to the powerful find command for handling complex directory operations!

Basic Directory Operations

Start with fundamental directory operations: listing contents, iteration, and counting items.

Output
Click Run to execute your code

Key Directory Commands

Command Purpose Common Options
ls List directory contents -l long, -a all, -h human
cd Change directory - previous, ~ home
pwd Print working directory -P physical path
mkdir Create directories -p parents, -m mode
rmdir Remove empty directories -p parents
Brace Expansion: Create multiple directories at once: mkdir -p project/{src,bin,docs,tests}. This creates all four subdirectories in a single command.

Recursive Directory Processing

Processing files in subdirectories requires recursive techniques. Here are four different approaches.

Output
Click Run to execute your code

Choosing the Right Method

Method Pros Cons
find -exec Handles special characters, powerful filters Spawns process per file
find | while Complex processing per file Subshell variable scope
Recursive function Full control, custom logic More code, stack limits
globstar ** Simple syntax, readable Bash 4+ only, less flexible
Performance Tip: For many files, use find ... -exec command {} + (plus instead of semicolon) to batch files into fewer command executions.

Advanced find Operations

The find command is incredibly powerful. Master these patterns for complex file operations.

Output
Click Run to execute your code

Essential find Expressions

# By type
find . -type f           # Regular files only
find . -type d           # Directories only
find . -type l           # Symbolic links

# By name (supports globs)
find . -name "*.log"     # Case-sensitive match
find . -iname "*.LOG"    # Case-insensitive

# By size
find . -size +100M       # Larger than 100MB
find . -size -1k         # Smaller than 1KB
find . -empty            # Zero-size files/dirs

# By time (days)
find . -mtime -7         # Modified in last 7 days
find . -atime +30        # Accessed over 30 days ago
find . -newer ref.txt    # Newer than ref.txt

# By permissions
find . -perm 755         # Exact permissions
find . -perm -u+x        # User executable

# Combining expressions
find . -type f -name "*.sh" -executable
find . \( -name "*.c" -o -name "*.h" \)
find . -type f ! -name "*.bak"

find Actions

# Print (default)
find . -name "*.txt" -print

# Execute command for each
find . -name "*.sh" -exec chmod +x {} \;

# Execute with confirmation
find . -name "*.bak" -ok rm {} \;

# Print with format
find . -type f -printf "%p %s bytes\n"

# Delete (careful!)
find . -name "*.tmp" -delete

# Batch execution (more efficient)
find . -name "*.c" -exec cat {} +
Caution: Always test find commands with -print before using -delete or destructive -exec actions. The deletion is immediate and permanent!

Practical Directory Patterns

# Find and delete empty directories
find . -type d -empty -delete

# Find large files
find . -type f -size +100M -exec ls -lh {} \;

# Find recently modified files
find . -type f -mmin -60  # Modified in last 60 minutes

# Find and replace in files
find . -name "*.txt" -exec sed -i 's/old/new/g' {} \;

# Find files not matching pattern
find . -type f ! -name "*.md" ! -name "*.txt"

# Count files by extension
find . -type f -name "*.js" | wc -l

# Find duplicate files by size
find . -type f -exec ls -l {} \; | awk '{print $5}' | sort | uniq -d

# Safe recursive delete with confirmation
find_delete() {
    find "$1" -name "$2" -print
    read -p "Delete these files? [y/N] " confirm
    [[ "$confirm" == [yY] ]] && find "$1" -name "$2" -delete
}

# Process files in parallel (GNU parallel)
# find . -name "*.jpg" | parallel convert {} -resize 50% resized/{}

Common Mistakes

1. Forgetting to quote glob patterns

# Wrong - shell expands glob before find runs
find . -name *.txt

# Correct - find receives the pattern
find . -name "*.txt"

2. Using semicolon without backslash

# Wrong - shell interprets semicolon
find . -exec echo {} ;

# Correct - escape or quote
find . -exec echo {} \;
find . -exec echo {} ';'

3. Iterating with ls output

# Wrong - breaks on spaces/special chars
for f in $(ls); do echo "$f"; done

# Correct - use glob directly
for f in *; do echo "$f"; done

# Or use find with null separator
find . -print0 | while IFS= read -r -d '' f; do
    echo "$f"
done

Exercise: Directory Statistics

Task: Create a script that generates directory statistics!

Requirements:

  • Count total files and directories
  • Calculate total size
  • Find largest file
  • Count files by extension
  • Show most recently modified files
Show Solution
#!/bin/bash
# Directory Statistics Tool

dir_stats() {
    local dir="${1:-.}"

    echo "=== Directory Statistics: $dir ==="
    echo ""

    # Count files and directories
    local files=$(find "$dir" -type f | wc -l)
    local dirs=$(find "$dir" -type d | wc -l)
    echo "Files: $files"
    echo "Directories: $dirs"
    echo ""

    # Total size
    local size=$(du -sh "$dir" 2>/dev/null | cut -f1)
    echo "Total size: $size"
    echo ""

    # Largest files
    echo "Top 5 largest files:"
    find "$dir" -type f -exec ls -lh {} \; 2>/dev/null | \
        sort -k5 -hr | head -5 | \
        awk '{print "  " $5 " " $9}'
    echo ""

    # Files by extension
    echo "Files by extension:"
    find "$dir" -type f -name "*.*" | \
        sed 's/.*\.//' | sort | uniq -c | \
        sort -rn | head -10 | \
        awk '{print "  " $1 " ." $2}'
    echo ""

    # Recently modified
    echo "Recently modified (last 24h):"
    find "$dir" -type f -mtime -1 -exec ls -lh {} \; 2>/dev/null | \
        head -5 | awk '{print "  " $6 " " $7 " " $8 " " $9}'
}

# Run on current directory or specified path
dir_stats "${1:-.}"

Summary

  • Basic Ops: Use ls, cd, pwd, mkdir -p for fundamentals
  • Iteration: Use globs (for f in *) for simple cases
  • Recursive: Use find for recursive processing with filters
  • globstar: Enable with shopt -s globstar for ** patterns
  • find Actions: Use -exec {} \; for per-file, {} + for batched
  • Safety: Test destructive commands with -print first

What's Next?

Congratulations on completing the File Operations module! You now have powerful skills for reading, writing, testing, and navigating files and directories. Next, explore Process Management to learn about job control, background processes, and signal handling!