Python pathlib: Manage Files and Paths Easily

Published on: July 10, 2026
Reading time: 5 minutes
Manipulação moderna de arquivos usando pathlib em Python

Filesystem paths look simple until a project has to run on Windows, macOS, and Linux. Manual string concatenation can introduce the wrong separator, duplicate slashes, unclear relative paths, and code that is difficult to read. Python pathlib solves these problems with objects that represent files and directories.

The central class is Path. A Path can be joined to another path, inspected, created, searched, read, written, renamed, moved, or deleted through clear methods and properties. Because pathlib is part of the standard library, there is no package to install.

This guide covers the everyday operations beginners need and finishes with a practical download organizer. It also explains when to combine pathlib with shutil and the Python os module.

Why Use pathlib Instead of Path Strings?

Traditional code often treats a path as ordinary text:

folder = "reports"
filename = "sales.csv"
path = folder + "/" + filename

That approach makes the programmer responsible for separators and edge cases. Pathlib expresses the same intention directly:

from pathlib import Path

path = Path("reports") / "sales.csv"
print(path)

The slash operator joins path components; it does not divide strings. The resulting object uses the conventions of the current operating system when converted to text.

The official Python pathlib documentation provides the complete API. The shutil documentation covers higher-level copying and moving operations used later in this guide.

Create Path Objects

from pathlib import Path

relative_path = Path("data") / "customers.csv"
current_directory = Path.cwd()
home_directory = Path.home()

print(relative_path)
print(current_directory)
print(home_directory)

Path.cwd() returns the current working directory. Path.home() returns the current user’s home directory. Neither should be confused with the folder containing the current script.

To find the script directory in a normal Python file:

script_directory = Path(__file__).resolve().parent
config_path = script_directory / "config" / "settings.json"

In notebooks and interactive shells, __file__ may not exist. Use Path.cwd() or an explicitly configured base directory there.

Absolute and Resolved Paths

path = Path("data") / ".." / "reports" / "sales.csv"

print(path.absolute())
print(path.resolve())

absolute() makes a path absolute without necessarily normalizing every component. resolve() returns an absolute, normalized path and can resolve symbolic links. Use resolve(strict=True) when the path must already exist.

When a script cannot locate a resource, the FileNotFoundError path guide provides a diagnostic checklist.

Inspect File and Directory Names

path = Path("exports") / "sales.2026.csv"

print(path.name)       # sales.2026.csv
print(path.stem)       # sales.2026
print(path.suffix)     # .csv
print(path.suffixes)   # ['.2026', '.csv']
print(path.parent)     # exports
print(path.parts)

with_name(), with_stem(), and with_suffix() return new Path objects. They do not rename anything on disk.

original = Path("report.txt")

print(original.with_name("summary.txt"))
print(original.with_stem("report-final"))
print(original.with_suffix(".csv"))

Check Whether a Path Exists

path = Path("config.json")

print(path.exists())
print(path.is_file())
print(path.is_dir())
print(path.is_symlink())

These methods are useful for friendly validation, but a file can change between checking and using it. Handle exceptions around the actual read, write, move, or delete operation when reliability matters.

Create Files and Directories

Create nested folders with mkdir():

output_directory = Path("project") / "exports" / "daily"
output_directory.mkdir(parents=True, exist_ok=True)

parents=True creates missing parent folders. exist_ok=True avoids an error if the destination directory already exists.

Create an empty file with touch():

log_file = Path("project.log")
log_file.touch(exist_ok=True)

Read and Write Text

settings = Path("settings.txt")

settings.write_text(
    "theme=dark\nlanguage=en\n",
    encoding="utf-8",
)

content = settings.read_text(encoding="utf-8")
print(content)

write_text() replaces the existing content. read_text() reads the entire file into memory, so use the regular open() pattern for very large files or streaming work.

For bytes:

binary_file = Path("data.bin")
binary_file.write_bytes(b"\x00\x01\x02")
data = binary_file.read_bytes()

Open a File Through a Path

csv_path = Path("data") / "customers.csv"

with csv_path.open(
    mode="r",
    encoding="utf-8",
    newline="",
) as file:
    for line in file:
        print(line.rstrip())

Path.open() accepts the same main arguments as the built-in open(). The context manager still guarantees that the file is closed.

List Directory Contents

iterdir() yields direct children of a directory:

directory = Path("downloads")

for item in directory.iterdir():
    kind = "folder" if item.is_dir() else "file"
    print(kind, item.name)

Sort the result when stable ordering matters:

for item in sorted(directory.iterdir()):
    print(item.name)

Find Files with glob() and rglob()

glob() applies a pattern from one directory. rglob() searches recursively.

project = Path("project")

for python_file in project.glob("*.py"):
    print(python_file)

for json_file in project.rglob("*.json"):
    print(json_file)

Use rglob() carefully on very large trees. Search only the directory you actually need, and consider ignoring virtual environments, caches, and generated files.

Rename and Replace Files

source = Path("draft.txt")
destination = Path("archive") / "final.txt"

destination.parent.mkdir(parents=True, exist_ok=True)
source.rename(destination)

rename() follows platform-specific behavior if the destination already exists. replace() more explicitly replaces an existing file or empty directory where the operating system permits it.

For moves between filesystems, use shutil.move(). The guide to copying and moving files with shutil covers file and directory copies, metadata, and safe destination handling.

Copy Files with pathlib and shutil

from pathlib import Path
import shutil

source = Path("reports") / "sales.csv"
destination = Path("backup") / source.name

destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)

Most standard-library functions that accept filesystem paths support Path objects directly. Converting every Path to str is usually unnecessary in modern Python.

Delete Files and Empty Directories

file_path = Path("temp.txt")

if file_path.is_file():
    file_path.unlink()

Remove an empty directory with rmdir():

empty_directory = Path("empty-folder")

if empty_directory.is_dir():
    empty_directory.rmdir()

These operations normally do not send items to the desktop recycle bin. Confirm the target and avoid constructing destructive paths from untrusted input. For a non-empty directory tree, shutil.rmtree() is powerful and should be protected by explicit safety checks.

Get File Metadata

from datetime import datetime
from pathlib import Path

path = Path("report.pdf")
metadata = path.stat()

print("Bytes:", metadata.st_size)
print(
    "Modified:",
    datetime.fromtimestamp(metadata.st_mtime),
)

stat() exposes size, timestamps, and permission-related metadata. Exact fields and timestamp semantics can vary by operating system.

Practical Project: Organize a Download Folder

This script groups files into category folders while avoiding accidental overwrites. It uses Path objects for inspection and shutil.move() for the move.

from pathlib import Path
import shutil


CATEGORIES = {
    "Images": {".jpg", ".jpeg", ".png", ".gif", ".webp"},
    "Documents": {".pdf", ".docx", ".txt", ".xlsx", ".csv"},
    "Archives": {".zip", ".rar", ".7z", ".tar", ".gz"},
    "Code": {".py", ".js", ".html", ".css", ".json"},
}


def choose_category(path):
    suffix = path.suffix.lower()

    for category, suffixes in CATEGORIES.items():
        if suffix in suffixes:
            return category

    return "Other"


def available_destination(folder, filename):
    candidate = folder / filename

    if not candidate.exists():
        return candidate

    stem = Path(filename).stem
    suffix = Path(filename).suffix
    number = 1

    while True:
        candidate = folder / f"{stem}-{number}{suffix}"

        if not candidate.exists():
            return candidate

        number += 1


def organize_directory(directory):
    directory = Path(directory).expanduser().resolve()

    if not directory.is_dir():
        raise NotADirectoryError(directory)

    moved = 0

    for item in list(directory.iterdir()):
        if not item.is_file():
            continue

        category = choose_category(item)
        destination_folder = directory / category
        destination_folder.mkdir(exist_ok=True)

        destination = available_destination(
            destination_folder,
            item.name,
        )

        shutil.move(item, destination)
        moved += 1
        print(f"Moved {item.name} to {category}/")

    return moved


if __name__ == "__main__":
    target = Path.home() / "Downloads"
    total = organize_directory(target)
    print(f"Finished: {total} files moved")

Test destructive or moving scripts on a temporary folder before pointing them at real files. For scheduled use, add Python logging so every move and failure has a persistent record. The Python backup guide is also useful before automating bulk changes.

Handle Filesystem Exceptions

from pathlib import Path

path = Path("protected") / "settings.txt"

try:
    text = path.read_text(encoding="utf-8")
except FileNotFoundError:
    print(f"Missing file: {path}")
except PermissionError:
    print(f"Permission denied: {path}")
except OSError as error:
    print(f"Filesystem error: {error}")

Catch specific exceptions when you can respond meaningfully. Avoid a broad except Exception that silently hides programming errors.

pathlib vs. os.path

Taskpathlibos.path
Join pathsbase / "file.txt"os.path.join(base, "file.txt")
Filenamepath.nameos.path.basename(path)
Parentpath.parentos.path.dirname(path)
Extensionpath.suffixos.path.splitext(path)[1]
Existencepath.exists()os.path.exists(path)

Pathlib is generally easier to read for modern path code. The os module guide remains important for environment variables, processes, permissions, and other operating-system interfaces.

Common Mistakes

  • Assuming Path.cwd() is the script directory.
  • Using with_suffix() and expecting the file to be renamed.
  • Calling read_text() on a very large file.
  • Deleting or moving files without checking the resolved destination.
  • Ignoring name collisions in an organizer script.
  • Using rename() for a move that crosses filesystems.
  • Assuming a file extension proves the file’s actual format.

Frequently Asked Questions

Do I need to install pathlib?

No. It is part of Python’s standard library.

Can I pass a Path to open()?

Yes. Most modern Python filesystem APIs accept path-like objects directly.

How do I join path components?

Use the slash operator: base / "folder" / "file.txt".

How do I get a path as a string?

Call str(path). Use path.as_posix() only when you specifically need forward-slash formatting.

How do I find every file recursively?

Use directory.rglob("*") and filter with is_file(), or use a more specific pattern such as rglob("*.csv").

Can pathlib work with URLs?

No. Pathlib represents filesystem paths, not web URLs. Use URL-specific libraries for HTTP and URL parsing.

Conclusion

Python pathlib makes filesystem code more expressive by turning paths into objects. Learn the slash operator, name, parent, suffix, mkdir(), iterdir(), glob(), read_text(), write_text(), rename(), and unlink() first.

Combine pathlib with shutil for high-level copying and moving, and with os for broader operating-system features. Most importantly, test bulk file operations on disposable data, resolve paths deliberately, and handle the exceptions that real filesystems can produce.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Barra de progresso com tqdm para scripts Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python tqdm: Add Progress Bars to Scripts

    Add progress bars to Python scripts with tqdm. Learn installation, loops, manual updates, files, pandas, nested bars, and practical options.

    Ler mais

    Tempo de leitura: 6 minutos
    10/07/2026
    Uso do módulo random para gerar valores aleatórios em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python random Module: Complete Beginner Guide

    Learn Python's random module: generate numbers, choose items, shuffle lists, sample data, use seeds, and know when to use secrets.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Manipulação de arquivos e pastas usando módulo os em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python os Module: Manage Files and Folders

    Learn the Python os module to list, create, rename, move, and remove files and folders, work with paths, and read

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Uso do Counter do collections para contar elementos em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python Counter: Count Items with collections

    Learn Python Counter to count items, find common values, update totals, compare counts, and solve practical frequency problems.

    Ler mais

    Tempo de leitura: 6 minutos
    10/07/2026
    Como evitar KeyError usando defaultdict em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Avoid Python KeyError with defaultdict

    Learn how defaultdict prevents Python KeyError, when to use default_factory, how it compares with dict.get, setdefault, Counter, and common mistakes.

    Ler mais

    Tempo de leitura: 7 minutos
    28/05/2026
    Introdução ao módulo itertools para iniciantes em Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python itertools: Practical Beginner Guide

    Learn Python itertools with practical examples of count, cycle, repeat, chain, combinations, permutations, groupby, tee, and memory-efficient iteration.

    Ler mais

    Tempo de leitura: 8 minutos
    28/05/2026