The Problem: A Chaotic Downloads Folder
Most developers have a Downloads folder that looks like a digital junk drawer — PDFs, images, ZIP archives, and installer files all mixed together. Manually sorting them is tedious and something you'll never do consistently. This is exactly the kind of repetitive task that automation was built for.
In this tutorial, we'll build a Python script that automatically sorts files into subfolders based on their file type. It's a practical, useful project that teaches real automation patterns.
What the Script Will Do
- Scan a target folder for files
- Detect each file's type based on its extension
- Move files into categorized subfolders (Images, Documents, Archives, etc.)
- Skip files that are already organized (idempotent behavior)
- Log what it did for auditing purposes
The Complete Script
import os
import shutil
import logging
from pathlib import Path
# Configure logging
logging.basicConfig(
filename="organizer.log",
level=logging.INFO,
format="%(asctime)s - %(message)s"
)
# Define file type categories
FILE_CATEGORIES = {
"Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"],
"Documents": [".pdf", ".docx", ".doc", ".txt", ".xlsx", ".csv"],
"Archives": [".zip", ".tar", ".gz", ".rar", ".7z"],
"Scripts": [".py", ".sh", ".js", ".ts", ".rb"],
"Audio": [".mp3", ".wav", ".flac", ".aac"],
"Video": [".mp4", ".mov", ".mkv", ".avi"],
}
def get_category(extension):
for category, extensions in FILE_CATEGORIES.items():
if extension.lower() in extensions:
return category
return "Other"
def organize_folder(target_path):
folder = Path(target_path)
for item in folder.iterdir():
if item.is_file():
category = get_category(item.suffix)
dest_folder = folder / category
dest_folder.mkdir(exist_ok=True)
dest = dest_folder / item.name
if not dest.exists():
shutil.move(str(item), str(dest))
logging.info(f"Moved: {item.name} -> {category}/")
if __name__ == "__main__":
import sys
path = sys.argv[1] if len(sys.argv) > 1 else "."
organize_folder(path)
print("Organization complete. Check organizer.log for details.")
Breaking Down the Key Parts
The FILE_CATEGORIES Dictionary
This maps folder names to lists of file extensions. It's easy to customize — just add new categories or extensions as needed. This is the single place you edit to change behavior.
The get_category() Function
This checks a file's extension against our dictionary and returns the appropriate category name. If no match is found, it returns "Other" — so nothing gets lost.
Idempotency Check
The line if not dest.exists() ensures the script won't overwrite files if you run it multiple times. Safe to run repeatedly.
Running the Script
Save the script as organizer.py and run it from your terminal:
# Organize your Downloads folder
python3 organizer.py ~/Downloads
# Organize the current directory
python3 organizer.py .
Taking It Further
- Schedule it — Add a cron job to run this nightly:
0 2 * * * python3 /path/to/organizer.py ~/Downloads - Watch for changes — Use the
watchdoglibrary to trigger organization automatically when new files appear. - Dry-run mode — Add a
--dry-runflag that prints what would happen without actually moving anything. Great for testing. - Duplicate detection — Hash files before moving to detect and handle duplicates gracefully.
Automation doesn't have to be complex. A focused, well-written 50-line script can save hours every month. Start here, then build from it.