Python pathlib: Manage Files and Paths

Updated on: August 20, 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

    Document and inbox representing local email stores with Python mailbox
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python mailbox: Local Email Stores

    Learn Python mailbox to read, create, and migrate Maildir, mbox, and MH stores with locking, flags, message parsing, and safe

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Text editor representing formatting with Python textwrap
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python textwrap: Format Text

    Learn Python textwrap to wrap, fill, shorten, indent, and dedent text with controlled width, whitespace, long words, and reusable settings.

    Ler mais

    Tempo de leitura: 4 minutos
    10/08/2026
    Folder and magnifying glass representing file-name filters with Python fnmatch
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python fnmatch: Filter File Names

    Learn Python fnmatch to filter file names with shell wildcards, control case sensitivity, exclude patterns, and distinguish glob from regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor with binary data representing compact numeric arrays in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python array: Compact Numeric Data

    Learn Python array for compact numeric storage, binary files, byte order, memory views, and safe buffer interoperability.

    Ler mais

    Tempo de leitura: 6 minutos
    10/08/2026
    Color wheel representing RGB, HSV, and HLS conversions with Python colorsys
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python colorsys: RGB, HSV, and HLS

    Learn Python colorsys to convert colors between RGB, HSV, HLS, and YIQ, generate palettes, and avoid scale and precision mistakes.

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Configuration icon representing plist files with Python plistlib
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    plistlib: Read and Write Apple Plist Files

    Learn Python plistlib to read and write XML and binary plist files, validate data, handle dates, bytes, and UIDs safely.

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026