🐍 Python Course · Stage 3 · Lesson 29 of 89 · os — OS Interface
Course Overview →

🔵 Read Beginner and Intermediate tabs. The Expert tab has the internals — worth reading once you finish Stage 3.

← Testing pathlib — File Paths →
thecodex.expert · The Codex Family of Knowledge
Python stdlib

Python os — Operating System Interface module

Create files, list directories, read environment variables — everything the OS exposes, from Python.

Python 3.13 Standard Library Last verified:
Canonical Definition

The Python os module provides a portable interface to operating system functionality — file and directory operations, environment variables, process information, and POSIX system calls — abstracting platform differences between Unix and Windows.

🟩 Beginner

Working with files, directories, and the OS

What you'll learn: The most commonly used os functions for path manipulation, listing directories, environment variables, and checking whether files and directories exist.

How to read this tab: Open a terminal alongside your reading. Try each function on your actual file system as you read about it.

⏱ 30 min 📄 3 sections 🔶 Prerequisite: File I/O
One sentence

The os module is Python's bridge to the operating system — it lets you create, read, move, and delete files and directories, read environment variables, and run system commands, all from Python code.

Use os.path.join() — never string concatenation. "data/" + "file.txt" breaks on Windows (uses backslashes). os.path.join("data", "file.txt") works on every OS. Even better: learn pathlib (next lesson) which makes path operations even cleaner with the / operator.

Working with paths

Pythonos_paths.py
import os

# Current working directory
cwd = os.getcwd()
print(cwd)              # /home/priya/projects

# Build paths portably (uses / on Unix, \ on Windows)
config_path = os.path.join(cwd, "config", "settings.json")
print(config_path)      # /home/priya/projects/config/settings.json

# Path components
path = "/home/priya/data/report.csv"
print(os.path.dirname(path))    # /home/priya/data
print(os.path.basename(path))   # report.csv
print(os.path.splitext(path))   # ('/home/priya/data/report', '.csv')
print(os.path.exists(path))     # True or False
print(os.path.isfile(path))     # True if it's a file
print(os.path.isdir(path))      # True if it's a directory
print(os.path.getsize(path))    # size in bytes
💡

Use os.makedirs(path, exist_ok=True) to create nested directories. os.mkdir() fails if the parent directory doesn't exist. os.makedirs() creates all intermediate directories. exist_ok=True means it won't raise an error if the directory already exists.

Creating and listing directories

Pythonos_dirs.py
import os

# Create a single directory
os.mkdir("new_folder")

# Create nested directories (like mkdir -p)
os.makedirs("parent/child/grandchild", exist_ok=True)
# exist_ok=True: no error if already exists

# List directory contents
entries = os.listdir(".")
print(entries)   # ['file1.txt', 'folder', 'script.py', ...]

# Walk entire directory tree
for dirpath, dirnames, filenames in os.walk("/home/priya/projects"):
    for filename in filenames:
        full_path = os.path.join(dirpath, filename)
        print(full_path)

# Rename / move
os.rename("old_name.txt", "new_name.txt")

# Delete file
os.remove("old_file.txt")

# Delete empty directory
os.rmdir("empty_folder")
💡

Always use os.environ.get() with a default, not os.environ[]. os.environ["MISSING_VAR"] raises KeyError. os.environ.get("MISSING_VAR", "default") returns the default safely. Environment variables are the standard way to configure applications in production.

Environment variables

Pythonos_env.py
import os

# Read environment variable (None if not set)
home = os.environ.get("HOME")
db_url = os.environ.get("DATABASE_URL", "sqlite:///default.db")

# Set environment variable (only in current process)
os.environ["DEBUG"] = "true"

# All environment variables
for key, value in os.environ.items():
    print(f"{key}={value}")

# Run a shell command (os.system is legacy — use subprocess instead)
exit_code = os.system("ls -la")   # not recommended for production

✅ Beginner tab complete — check your understanding

  • I can use os.path.join() to build file paths cross-platform
  • I can use os.listdir() or os.walk() to list files in a directory
  • I can read and write environment variables with os.environ
  • I know os.path.exists(), os.path.isfile(), os.path.isdir()

Continue to pathlib →

🔵 Intermediate

os.walk, os.scandir, and process information

What you'll learn: os.walk for recursively traversing directory trees. os.scandir as the faster, more informative alternative to os.listdir. Process information via os.getpid, os.cpu_count, and os.stat.

How to read this tab: Try os.walk on your project directory and print every file path. This pattern appears in almost every file-processing script.

⏱ 25 min 📄 2 sections 🔶 Prerequisite: Beginner tab
💡

For new code: use pathlib. pathlib (the next lesson) provides the same functionality as os.path but with an object-oriented, much cleaner API. You'll still encounter os.path in existing codebases, so knowing it is essential — but write new code with pathlib.

os.path vs. pathlib

os.path works with plain strings. pathlib (Python 3.4+) uses objects that are more readable and composable. In new code, prefer pathlib. os is still needed for environment variables, process management, file permissions, and low-level OS operations that pathlib does not cover.

📎

Prefer os.scandir() over os.listdir() for performance. os.scandir() returns DirEntry objects which cache stat info — so checking is_file() on an entry doesn't make another system call. For large directories, this is significantly faster.

os.scandir — the efficient directory lister

Pythonos_scandir.py
import os

# os.scandir() is faster than os.listdir() — returns DirEntry objects
# DirEntry has .name, .path, .is_file(), .is_dir(), .stat()

with os.scandir(".") as entries:
    for entry in entries:
        if entry.is_file():
            stat = entry.stat()
            print(f"{entry.name}: {stat.st_size} bytes")

# Find all Python files recursively
def find_py_files(root):
    for entry in os.scandir(root):
        if entry.is_dir(follow_symlinks=False):
            yield from find_py_files(entry.path)
        elif entry.name.endswith(".py"):
            yield entry.path

for py_file in find_py_files("."):
    print(py_file)
📎

Useful for scripts and servers. os.getpid() gives the current process ID. os.cpu_count() tells you how many CPUs are available (useful when setting ThreadPoolExecutor or ProcessPoolExecutor worker counts). os.uname() gives OS info on Unix systems.

Process and system information

Pythonos_process.py
import os

# Current process
print(os.getpid())    # process ID
print(os.getppid())   # parent process ID (Unix)

# User and group (Unix)
print(os.getuid())    # user ID
print(os.getgid())    # group ID

# CPU count
print(os.cpu_count())   # number of CPUs

# File permissions
os.chmod("script.py", 0o755)   # rwxr-xr-x

# Symbolic links
os.symlink("target.txt", "link.txt")   # create symlink
os.readlink("link.txt")                 # read symlink target

# Temporary files — prefer tempfile module
import tempfile
with tempfile.NamedTemporaryFile(delete=False, suffix=".tmp") as f:
    f.write(b"temporary data")
    print(f.name)   # /tmp/tmpXXXXXX.tmp
Commonly confused
os.path.join() ignores everything before an absolute path component. os.path.join("/home/user", "/etc/config") returns "/etc/config" — the absolute second argument discards the first. This is a common source of path bugs. Validate that dynamic path components are relative before joining.
os.system() is not the right way to run commands. It gives you only the exit code, not the output. Use the subprocess module (subprocess.run(), subprocess.check_output()) for anything serious — it gives full control over stdin/stdout/stderr, timeouts, and error handling.
os.makedirs() without exist_ok=True raises FileExistsError. Always pass exist_ok=True when the directory may already exist — which is true in almost every real scenario.

✅ Intermediate tab complete — check your understanding

  • I can use os.walk to process every file in a directory tree recursively
  • I know that os.scandir() returns DirEntry objects with cached stat info — faster than os.listdir()

Continue to pathlib →

🔴 Expert

POSIX compliance, inodes, and file metadata

What you'll learn: POSIX standard and platform differences (Windows vs Unix). File metadata via os.stat() — inode numbers, modification times, permissions. os.open() for low-level file descriptor operations.

How to read this tab: Read when working on cross-platform tools or when you need to check file metadata beyond just existence.

⏱ 20 min 📄 2 sections 🔶 Prerequisite: After Stage 2

POSIX compliance and platform differences

The os module exposes POSIX-defined functions on Unix systems and Win32 equivalents on Windows. Where behaviour differs, os.name is 'posix' on Unix/macOS and 'nt' on Windows. os.sep is '/' on Unix and '\\' on Windows — which is why you should always use os.path.join() or pathlib.Path rather than string concatenation. The os module documentation explicitly notes which functions are only available on certain platforms.

inode, st_mtime, and file metadata

os.stat(path) returns a os.stat_result object. Key fields: st_size (bytes), st_mtime (modification time as Unix timestamp), st_ctime (creation time on Windows; inode change time on Unix — not creation time), st_ino (inode number), st_mode (permission bits — interpret with stat module), st_nlink (hard link count). Note: st_ctime is not "creation time" on Unix — this is a common misconception documented in the Python docs.

✅ Expert tab complete

  • I know what an inode is and how to get file metadata with os.stat()
  • I understand the key platform differences between Windows and POSIX systems

Continue to pathlib →

Sources

1
Python Standard Library — os. docs.python.org/3/library/os.html.
2
POSIX.1-2017. pubs.opengroup.org. — Underlying specification for os module on Unix.
3
Python Standard Library — os.path. docs.python.org/3/library/os.path.html.
Source confidence: High Last verified: Primary source: docs.python.org/3/library/os.html

Sources

1
Python Standard Library — os. docs.python.org/3/library/os.html.
2
Python Standard Library — os.path. docs.python.org/3/library/os.path.html.
3
POSIX.1-2017. pubs.opengroup.org.