Python File and Directory Operations

Build paths, create folders, list and search files, and move or delete them safely with pathlib and shutil.

Lesson 50 of 107Intermediate11 min read

Python's pathlib module handles files and folders through Path objects. You build paths with /, create folders with mkdir(), list them with iterdir() and glob(), and move or delete entries with rename(), unlink() and rmdir():

Python
from pathlib import Path
archive = Path("photos") / "2026"
archive.mkdir(parents=True, exist_ok=True)
for image in sorted(archive.glob("*.png")):
    print(image.name)

This lesson covers each step and the safety habits that matter when a program changes the file system: a typo in a delete command has no undo. The examples organise a photo archive, and every runnable demonstration works inside a temporary folder that is removed afterwards.

Path objects

A Path represents a location on disk. Joining with / uses the correct separator on every operating system, so the same code works on Windows (\) and macOS or Linux (/):

Python
from pathlib import Path
project = Path("projects") / "regatta" / "photos"
print(project.name, project.parent, project.suffix)   # photos projects/regatta (empty)
image = project / "finish-line.png"
print(image.stem, image.suffix)                       # finish-line .png

Useful attributes and methods include .name, .stem, .suffix, .parent, .exists(), .is_file(), .is_dir(), .resolve() (absolute path) and Path.home() and Path.cwd().

Creating a Path object doesn't touch the disk; it's only a description of a location. Path("missing.txt") is a perfectly valid object for a file that doesn't exist. Check with .exists(), or just try the operation and handle the error. Older code uses os.path.join() and string paths; they still work, and most functions accept either form.

Directory creation

mkdir() creates a folder. Two options make it practical:

Python
archive = Path("archive") / "2026" / "july"
archive.mkdir(parents=True, exist_ok=True)
  • parents=True creates any missing parent folders, like mkdir -p.
  • exist_ok=True makes it succeed quietly if the folder is already there, so the script can run repeatedly.

Without exist_ok, an existing folder raises FileExistsError. Even with it, if a file (not a folder) already has that name, mkdir still fails, because a file can't be turned into a directory.

For scratch work, tempfile.TemporaryDirectory() creates a unique folder and deletes it, with everything inside, when the with block ends. It's the safest place for tests, experiments and intermediate files.

Traversal

Three methods list a folder's contents:

MethodReturns
folder.iterdir()every immediate child (files and folders)
folder.glob("*.png")children matching a pattern
folder.rglob("*.png")matches in this folder and all sub-folders
Python
pngs = sorted(p for p in archive.glob("*.png") if p.is_file())
total = sum(p.stat().st_size for p in pngs)

Patterns use * (any characters), ? (one character) and [abc] (one of a set); ** in glob() means "any depth of folders". All three return iterators, and the order is not guaranteed: it depends on the operating system and file system. Sort the results whenever order matters for display, reproducibility or tests.

os.walk() is the older alternative that yields each folder with lists of its sub-folders and files, which is still handy when you need to process a tree folder by folder.

Moves and deletion

TaskpathlibNotes
Rename or move a filep.rename(target)returns the new path
Replace an existing targetp.replace(target)overwrites without asking
Delete a filep.unlink(missing_ok=True)permanent
Delete an empty folderp.rmdir()fails if not empty
Delete a folder treeshutil.rmtree(p)deletes everything inside
Copy a file / treeshutil.copy2(p, t) / shutil.copytree(p, t)keeps metadata

rename() may fail across different drives; shutil.move() handles that case by copying and deleting.

Before any recursive delete, check the target: print it, make sure it's inside the folder you expect (target.resolve().is_relative_to(base.resolve()) in Python 3.9+), and never build it from unchecked user input. Scripts that delete things are often run with a "dry run" option first, which prints what would be removed without doing it.

Independent practice and a worked check

Task: create and rename sample files inside TemporaryDirectory, then explain the difference between unlink and rmdir.

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    folder = Path(tmp) / "drafts"
    folder.mkdir()
    draft = folder / "draft.txt"
    draft.write_text("v1", encoding="utf-8")
    final = draft.rename(folder / "final.txt")
    print(sorted(p.name for p in folder.iterdir()))   # ['final.txt']
    final.unlink()          # removes a file
    folder.rmdir()          # removes an empty directory
    print(folder.exists())  # False

unlink() deletes a file; rmdir() deletes a folder only when it's empty, which is why the file had to go first. For the worked check: does creating a Path object create a filesystem entry? No. Only methods such as mkdir(), touch() or write_text() change the disk.

Reference and next practice

The pathlib documentation and shutil documentation cover every operation, including the table mapping os.path functions to pathlib. Continue with the standard library tour and environment configuration. Practise with the Python exercises and Python quiz.

File and Directory Operations Summary

  • Path objects: pathlib.Path expresses filesystem paths and joins components with the slash operator.
  • Directory creation: mkdir can create parents and tolerate an already existing directory when configured.
  • Traversal: iterdir lists immediate children while glob can select matching names.
  • Moves and deletion: rename moves a path while unlink removes files and rmdir removes empty directories.

File and Directory Operations Examples

Each example shows the code, the output it produces and what each part does.

Example 1

File and Directory Operations in a small program

Python
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
    archive = Path(folder) / "photos"
    archive.mkdir()
    (archive / "sample.txt").write_text("Frame 8", encoding="utf-8")
    print(sorted(p.name for p in archive.iterdir()))
Output
['sample.txt']
Line-by-line explanation
  1. Line 3TemporaryDirectory() creates a fresh folder that is removed at the end of the block.
  2. Lines 4 to 5A Path is built with / and the folder is created with mkdir().
  3. Line 6write_text() creates a file inside it.
  4. Line 7iterdir() lists the folder; sorting the names makes the output predictable.
Example 2

Organising an archive by file type

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    inbox = Path(tmp) / "inbox"
    inbox.mkdir()
    for name in ["start.png", "finish.png", "notes.txt", "crew.jpg"]:
        (inbox / name).write_text("demo", encoding="utf-8")

    for item in sorted(inbox.iterdir()):
        target_dir = Path(tmp) / "sorted" / item.suffix.lstrip(".")
        target_dir.mkdir(parents=True, exist_ok=True)
        item.rename(target_dir / item.name)

    for path in sorted(Path(tmp).rglob("*")):
        if path.is_file():
            print(path.relative_to(tmp))
    print("Inbox empty:", not any(inbox.iterdir()))
    inbox.rmdir()
    print("Inbox removed:", not inbox.exists())
Output
sorted/jpg/crew.jpg
sorted/png/finish.png
sorted/png/start.png
sorted/txt/notes.txt
Inbox empty: True
Inbox removed: True
Line-by-line explanation
  1. Line 11The extension, without its dot, names the destination folder.
  2. Line 12parents=True, exist_ok=True creates "sorted/png" the first time and ignores it afterwards.
  3. Lines 15 to 17rglob("*") walks every sub-folder; relative_to() shortens the printed paths.
  4. Line 19rmdir() only succeeds because the folder is now empty.

Common File and Directory Operations Mistakes

Building paths with string concatenation

Don't
folder = "photos"
path = folder + "/" + "2026" + "/" + "start.png"   # wrong separator on some systems, easy to double up
Do
from pathlib import Path
path = Path("photos") / "2026" / "start.png"

Deleting a tree without checking the target

Don't
import shutil
shutil.rmtree(user_supplied_path)      # could be anything, including your home folder
Do
import shutil
from pathlib import Path
base = Path("archive").resolve()
target = (base / user_supplied_name).resolve()
if target.is_relative_to(base) and target != base:
    shutil.rmtree(target)

Restrict deletions to a known folder and resolve the path first so .. can't escape it.

File and Directory Operations in Real Projects

File system automation

Organising downloads, archiving logs by month, generating output folders for each report, cleaning build directories and backing up project files are classic Python automation tasks. pathlib makes these scripts short and portable, and shutil adds copying, moving and archiving (shutil.make_archive() creates ZIP files).

Professional tools add safety: they log every change, support a dry-run mode, refuse to operate outside a configured root folder, and process files in sorted order so runs are reproducible. Test suites use TemporaryDirectory or pytest's tmp_path fixture so tests never touch real user files.

File and Directory Operations FAQ

Should I use pathlib or os.path?

Use pathlib for new code. It is object-oriented, reads more naturally (folder / "file.txt") and groups related operations on one object. os.path still works and appears in older code; most standard functions accept Path objects as well as strings.

How do I list all files in a directory in Python?

[p for p in Path("folder").iterdir() if p.is_file()] lists the files in one folder. Use Path("folder").rglob("*") to include sub-folders, and glob("*.csv") to filter by pattern. Sort the result if order matters.

How do I create a folder only if it doesn't exist?

Path("folder").mkdir(parents=True, exist_ok=True). It creates missing parents and does nothing if the folder already exists.

How do I delete a folder that isn't empty?

shutil.rmtree(path) deletes the folder and everything inside it, permanently and without confirmation. Path.rmdir() only removes empty folders. Double-check the path before calling rmtree.

How do I get a file's extension in Python?

Path("photo.final.png").suffix returns ".png", and .suffixes returns all of them, ['.final', '.png']. .stem gives the name without the last extension.

File and Directory Operations Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

Does creating a Path object create a filesystem entry?

Multiple choicemedium

Which review concern is valid for file and directory operations?

File and Directory Operations Quiz

2 questions to check what you learned.

  1. Does creating a Path object create a filesystem entry?

    • Yes
    • No
    • Python ignores the distinction
  2. Which statement correctly describes traversal?

    • iterdir lists immediate children while glob can select matching names.
    • Only the example output matters, not the rule
    • Every operation has the same behavior