Everyone has that one folder. Downloads, Desktop, some project directory — hundreds of files, no structure, and the thought of sorting it by hand is so dull you just... don't.
I wrote a small Python script that does it for you. It sorts a folder's files into subfolders by type, and the important part is in the title: by default, it changes nothing. It only shows you what it would do. You have to explicitly tell it to move files.
What it does
Point it at a folder and it sorts every file into one of six categories:
| Folder | Extensions |
|---|---|
| Images | .jpg, .jpeg, .png, .gif, .webp, .svg |
| Documents | .pdf, .doc, .docx, .txt, .md |
| Spreadsheets | .csv, .xls, .xlsx, .ods |
| Archives | .zip, .tar, .gz, .rar, .7z |
| Code | .py, .js, .html, .css, .json |
| Others | anything else |
No dependencies beyond the standard library — just argparse, shutil, and pathlib. No install step at all.
Usage
python file_organizer.py ~/Downloads # dry run, no changes python file_organizer.py ~/Downloads --apply # actually move files The dry run prints lines like would move: report.pdf -> Documents/ and finishes with a count. Only --apply performs the moves.
How it works
The whole script is built around one mapping and one function:
CATEGORIES = { "Images": {".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"}, "Documents": {".pdf", ".doc", ".docx", ".txt", ".md"}, "Spreadsheets": {".csv", ".xls", ".xlsx", ".ods"}, "Archives": {".zip", ".tar", ".gz", ".rar", ".7z"}, "Code": {".py", ".js", ".html", ".css", ".json"}, } def category_for(path): """Return the category folder name for a file's extension.""" for name, extensions in CATEGORIES.items(): if path.suffix.lower() in extensions: return name return "Others" Extensions are lowercased before matching, so Photo.JPG and photo.jpg land in the same folder. Anything unknown falls into Others rather than being skipped silently — a file you can't find is worse than a file in a misc folder.
The organising function keeps the dry-run and apply paths in one place, so they can never drift apart:
def organize(folder, apply=False): folder = Path(folder) if not folder.is_dir(): print(f"error: {folder} is not a folder", file=sys.stderr) return 1 moved = 0 for path in sorted(folder.iterdir()): if not path.is_file(): continue # never touch subfolders target_dir = folder / category_for(path) target = target_dir / path.name if apply: target_dir.mkdir(exist_ok=True) shutil.move(str(path), str(target)) print(f"moved: {path.name} -> {target_dir.name}/") else: print(f"would move: {path.name} -> {target_dir.name}/") moved += 1 mode = "moved" if apply else "would move (dry run — use --apply to move)" print(f"{moved} file(s) {mode}") return 0 Three safety decisions are doing the quiet work here:
- Dry run is the default. The destructive action requires an explicit flag. If you run it by accident, nothing happens — you just get a preview.
- It never touches subfolders. Only files directly in the folder are moved. Your existing structure stays intact.
- It never deletes. Files are moved, not removed. If a category looks wrong, everything is still there, one folder away.
Three lessons I'd pass on
- Make the safe behaviour the default. A script that moves files should have to be talked into it. Dry-run-first is a design decision, not a feature — and it's the reason I'd trust this on a real Downloads folder.
- Small scripts beat small apps. This is about 70 lines and has no dependencies. It doesn't need a GUI, a config file, or a scheduler to be useful.
- Print what you're doing. Every move gets a line of output, and the run ends with a count. When a script touches your files, silence is a bug.
This is the second tool in my Python Automation Toolkit — the first scrapes a catalogue into a clean Excel report, and this one tidies the folder that report lands in. Next, I'll write about the project that taught me the most: a RAG-powered assistant that answers questions about Pakistani electricity bills.
Malik Ali Hassan — .NET & full-stack developer, final-year Software Engineering student. I build Python automation, AI assistants, and web apps.


