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 watchdog library to trigger organization automatically when new files appear.
  • Dry-run mode — Add a --dry-run flag 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.