Working with Directories
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.
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 |
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.
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 |
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.
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 {} +
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 -pfor fundamentals - Iteration: Use globs (
for f in *) for simple cases - Recursive: Use
findfor recursive processing with filters - globstar: Enable with
shopt -s globstarfor**patterns - find Actions: Use
-exec {} \;for per-file,{} +for batched - Safety: Test destructive commands with
-printfirst
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!
Enjoying these tutorials?