How to Auto Organize Downloads by File Extension in Python

A Python script that sorts files in a directory into subfolders based on their file extensions, creating folders automatically.

Easy Python 3.9+ Aug 9, 2026 Automation & scripting 13 views 0 copies

Python code

41 lines
Python 3.9+
import os
import shutil
from pathlib import Path

def organize_downloads(download_dir="~/Downloads"):
    """Move files in a directory into subfolders based on file extension."""
    download_path = Path(download_dir).expanduser()
    
    if not download_path.exists():
        print(f"Directory not found: {download_path}")
        return

    for item in download_path.iterdir():
        if item.is_file():
            ext = item.suffix.lower() or "no_extension"
            ext_folder = ext.lstrip(".") + "_files"
            target_dir = download_path / ext_folder
            target_dir.mkdir(exist_ok=True)
            
            target_path = target_dir / item.name
            if not target_path.exists():
                shutil.move(str(item), str(target_path))
                print(f"Moved: {item.name} -> {ext_folder}/{item.name}")
            else:
                print(f"Skipped (already exists): {item.name}")

if __name__ == "__main__":
    # Example: test with a temporary directory
    import tempfile
    with tempfile.TemporaryDirectory() as tmp:
        p = Path(tmp)
        (p / "report.pdf").write_text("PDF content")
        (p / "image.png").write_bytes(b"\x89PNG\r\n")
        (p / "notes.txt").write_text("Hello")
        (p / "README").write_text("No extension")
        
        organize_downloads(tmp)
        print("\nOrganized structure:")
        for dirpath, dirnames, filenames in os.walk(tmp):
            for name in filenames:
                print(os.path.relpath(os.path.join(dirpath, name), tmp))

Output

stdout
Moved: report.pdf -> pdf_files/report.pdf
Moved: image.png -> png_files/image.png
Moved: notes.txt -> txt_files/notes.txt
Moved: README -> no_extension_files/README

Organized structure:
README
report.pdf
notes.txt
image.png
no_extension_files/README
pdf_files/report.pdf
png_files/image.png
txt_files/notes.txt

How it works

The script uses pathlib.Path for clean path handling and expanduser() to resolve the ~ shortcut. It iterates through files in the target directory, extracts the lowercase file extension, and creates a folder named {extension}_files for each type. Files without an extension are placed in a no_extension_files folder. The shutil.move() function handles the file transfer, and the script skips files that would overwrite existing ones in the destination.

Common mistakes

  • Forgetting `.expanduser()` when using `~` in the path, which causes the script to fail with a literal `~` directory
  • Not checking if the destination file already exists, which can silently overwrite files with the same name
  • Using `os.path` functions instead of `pathlib`, making the code more verbose and harder to read

Variations

  1. Use `glob` or `os.scandir` for more control when filtering specific file types
  2. Apply a timestamp prefix to files that already exist to avoid skipping them

Real-world use cases

  • Running a scheduled cleanup script that keeps a cluttered downloads folder organized without manual effort.
  • Automating file sorting for media folders where photos, videos, and documents arrive from multiple sources.
  • Integrating with a file-watcher service that triggers organization whenever new files are added to a shared directory.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Automation & scripting

Related tutorials and quizzes for this topic.