os

One module, three jobs: talk to the environment (os.environ, getenv), manage files and folders (listdir, scandir, walk, makedirs, remove, replace, stat), and drive processes and file descriptors. Most of it is portable; a large part is Unix only, and every page here says which.

SystemPython 3.0+Live demo
Import
import os
from os import path, environ
import os.path
Size
os.__all__ has 134 names on Windows (CPython 3.13) and around 390 on Linux — most of the difference is Unix process, scheduling and flag constants
os.path
posixpath on Linux and macOS, ntpath on Windows — see the os.path module
Errors
Everything raises OSError subclasses: FileNotFoundError, FileExistsError, PermissionError, NotADirectoryError, IsADirectoryError …
Paths
Every path argument accepts str, bytes or an os.PathLike such as pathlib.Path

Demo

Live evaluation
Create a few empty files (folders are made on the way), then list one folder. listdir order is arbitrary, so the result is sorted.
Try:
Inputs
fileslist[str]files to create, comma-separated
folderstrfolder to list
Code
import os
for f in ['README.md', 'src/app.py', 'src/util.py']:
    os.makedirs(os.path.dirname(f) or '.', exist_ok=True)
    open(f, 'w').close()
try:
    result = sorted(os.listdir('.'))
except OSError as e:
    result = type(e).__name__
result
Result
['README.md', 'src']

listdir returns bare names — files and folders mixed, in no promised order — and raises FileNotFoundError for a missing folder and NotADirectoryError for a file (the message text differs between Linux and Windows; the class does not). In the environment tab, environment values must be str: os.environ['APP_MODE'] = 8080 raises TypeError: str expected, not int. getenv returns None, or your default, for a variable that is not set.

Members

Functions38
os.access
access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True)
Ask whether the current user may read, write or execute a path (or whether it exists) - with the mode flags F_OK, R_OK, W_OK and X_OK.
os.chmod
chmod(path, mode, *, dir_fd=None, follow_symlinks=True) / fchmod / lchmod / chown / fchown / lchown / umask
Change permission bits (chmod, fchmod, lchmod), file owner and group (chown, fchown, lchown), and the default mask for new files (umask).
os.cpu_count
cpu_count() / os.process_cpu_count() / os.getloadavg() / os.times()
How much CPU there is and how much you used: logical CPU count (or None), the CPUs this process may use (3.13+), the Unix load average, and user/system/elapsed process times.
os.eventfd
eventfd(initval[, flags=os.EFD_CLOEXEC]) -> int
Linux special file descriptors: eventfd (a 64-bit counter you can select/poll on), memfd_create (an anonymous in-memory file) and timerfd (a timer that becomes readable when it fires, 3.13+).
os._exit
_exit(n) / os.EX_OK, os.EX_USAGE, os.EX_DATAERR, ... (sysexits exit codes)
Exit the process immediately with status n - no finally blocks, no atexit handlers, no flushing of stdio buffers. The EX_* constants are the conventional exit codes from sysexits.h (EX_OK = 0).
os.execv
execv(path, args) / execve(path, args, env) / execvp(file, args) / execvpe(file, args, env) / execl(path, arg0, ...) / execle / execlp / execlpe / os.get_exec_path(env=None)
Replace the running Python process with another program - the exec family never returns. l/v say how arguments are passed, p searches PATH, e supplies a new environment. get_exec_path() lists the directories a p-variant searches.
os.fork
fork() / os.forkpty() / os.register_at_fork(*, before=None, after_in_parent=None, after_in_child=None)
Clone the running process (Unix only): fork() returns 0 in the child and the child pid in the parent. forkpty() adds a pseudo-terminal, register_at_fork() installs hooks around every fork.
LIVE
os.fspath
fspath(path) / os.fsencode(filename) / os.fsdecode(filename)
Turn any path-like object into a plain str or bytes (fspath), and convert file names between str and bytes with the file system encoding (fsencode, fsdecode).
os.fsync
fsync(fd)
Force a file’s written data out of the OS cache onto the disk. Plus the other descriptor-level file controls: fdatasync, sync, ftruncate / truncate, posix_fallocate, posix_fadvise and lockf.
os.get_terminal_size
get_terminal_size(fd=STDOUT_FILENO, /)
Ask the terminal behind a file descriptor for its size, as an os.terminal_size(columns, lines) tuple. Raises OSError when the fd is not a terminal; shutil.get_terminal_size() is the forgiving high-level version.
os.getcwd
getcwd() / os.getcwdb() / os.chdir(path) / os.fchdir(fd) / os.chroot(path)
Read the current working directory (as str or bytes) and change it - by path, by open directory descriptor, or change the root directory itself.
os.getpid
getpid() / os.getppid() / os.getpgid(pid) / os.getpgrp() / os.getsid(pid, /) / os.setpgid(pid, pgrp, /) / os.setpgrp() / os.setsid()
Process ids: your own pid and your parent pid (Unix and Windows), plus the Unix process-group and session calls used by shells and daemons.
os.getuid
getuid() / os.geteuid() / os.getgid() / os.getegid() / os.getgroups() / os.setuid(uid, /) / ...
Unix user and group ids of the process: real, effective and saved uid/gid, supplementary groups, and the set* calls that drop privileges. Plus os.getlogin() (Unix and Windows).
os.getxattr
getxattr(path, attribute, *, follow_symlinks=True) / setxattr / listxattr / removexattr
Read, write, list and delete Linux extended attributes - small named byte values stored with a file (user.*, security.*, trusted.*, system.*).
os.kill
kill(pid, sig, /) / os.killpg(pgid, sig, /) / os.abort()
Send a signal to a process (kill) or a whole process group (killpg, Unix), or abort the current process at once with SIGABRT. On Windows os.kill terminates the process unless the signal is CTRL_C_EVENT or CTRL_BREAK_EVENT.
LIVE
os.listdir
listdir(path='.') / os.scandir(path='.') → DirEntry
List a folder: listdir returns the entry names, scandir returns DirEntry objects that already know whether each entry is a file or a folder.
os.listdrives
listdrives() / os.listvolumes() / os.listmounts(volume)
Windows only: list drive roots ('C:\\'), volume GUID paths, and the mount points of a volume.
LIVE
os.makedirs
makedirs(name, mode=0o777, exist_ok=False) / os.mkdir(path, mode=0o777, *, dir_fd=None) / os.rmdir(path) / os.removedirs(name)
Create folders: mkdir makes one level, makedirs makes every missing parent too (exist_ok=True: no error if it is already there). rmdir and removedirs delete empty folders.
os.mknod
mknod(path, mode=0o600, device=0, *, dir_fd=None) / os.mkfifo(path, mode=0o666) / os.major / os.minor / os.makedev
Create special filesystem nodes - named pipes (mkfifo), device files and plain files (mknod) - and split or build raw device numbers (major, minor, makedev).
os.nice
nice(increment, /) / os.getpriority(which, who) / os.setpriority(which, who, priority)
Make a process nicer to others: nice() adds to the niceness of the current process, getpriority / setpriority read and set it for a process, process group or user (PRIO_PROCESS, PRIO_PGRP, PRIO_USER). Unix only.
os.open
open(path, flags, mode=0o777, *, dir_fd=None)
Open a file at the OS level and get a raw integer file descriptor; read, write, lseek and close work on that number. fdopen wraps it in a normal file object.
os.openpty
openpty() / os.isatty(fd, /) / os.ttyname(fd, /) / os.posix_openpt(oflag, /) / ...
Terminals and pseudo-terminals: open a pty pair, ask whether an fd is a terminal (isatty, also on Windows) and which one (ttyname, ctermid), its encoding, its foreground process group, and the 3.13 POSIX calls posix_openpt / grantpt / unlockpt / ptsname.
os.pipe
pipe() -> (r, w)
Create an OS pipe: two file descriptors, bytes written to w come out of r. Plus the descriptor tools around it — dup, dup2, the inheritable flag and blocking mode.
os.pread
pread(fd, n, offset, /) -> bytes
Unix positional and vectored I/O: read or write at an offset without moving the file position (pread, pwrite), scatter/gather over several buffers (readv, writev, preadv, pwritev), and kernel-side copies (sendfile, splice, copy_file_range).
LIVE
os.remove
remove(path) / os.unlink(path) / os.rename(src, dst) / os.replace(src, dst) / os.renames(old, new)
Delete a file (remove / unlink) and rename or move one (rename, replace, renames). replace overwrites an existing target on every OS; rename does not on Windows.
os.sched_getaffinity
sched_getaffinity(pid, /) / os.sched_setaffinity(pid, mask, /) / os.sched_setscheduler(pid, policy, param, /) / ...
The scheduler interface: which CPUs a process may run on (affinity), its scheduling policy (SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR) and priority (sched_param), and sched_yield(). Available only on some Unix platforms, mainly Linux.
os.spawnv
spawnv(mode, path, args) / spawnve / spawnvp / spawnvpe / spawnl(mode, path, ...) / spawnle / spawnlp / spawnlpe / os.posix_spawn(path, argv, env, *, file_actions=None, ...) / posix_spawnp
Start a program in a new process: with P_WAIT you get its exit code, with P_NOWAIT its process id (a handle on Windows). posix_spawn/posix_spawnp are the Unix C posix_spawn() API. subprocess is the recommended replacement for all of them.
LIVE
os.stat
stat(path, *, dir_fd=None, follow_symlinks=True) → stat_result / os.lstat(path) / os.fstat(fd) / os.utime(path, times=None, *, ns=…)
Read a file's metadata — size, type and permission bits, timestamps, inode — as an os.stat_result. lstat does not follow symlinks, fstat works on an open file descriptor, utime sets the access and modification times.
os.statvfs
statvfs(path) / os.fstatvfs(fd) → os.statvfs_result
Filesystem statistics for the filesystem holding a path or open file: block size, total/free/available blocks, inodes, mount flags (ST_*) and the maximum file name length.
os.strerror
strerror(code, /) / os.error
Turn an errno number into the C library's error message, and os.error - the old alias of the built-in OSError.
os.symlink
symlink(src, dst, target_is_directory=False, *, dir_fd=None) / os.link(src, dst) / os.readlink(path)
Create symbolic links (symlink) and hard links (link), and read where a symbolic link points (readlink).
os.sysconf
sysconf(name, /) / os.confstr(name, /) / os.pathconf(path, name) / os.fpathconf(fd, name, /)
Query POSIX system configuration: integer limits with sysconf (page size, clock ticks, CPUs), string values with confstr, per-file limits with pathconf / fpathconf. The *_names dicts list the names the system knows. Unix only.
os.system
system(command) / os.popen(cmd, mode='r', buffering=-1) / os.startfile(path[, operation][, arguments][, cwd][, show_cmd])
Run a shell command: system() returns only a status, popen() gives you a file to read its output from, startfile() opens a file with its Windows-associated app. subprocess replaces all three.
os.uname
uname()
Identify the operating system kernel: sysname, nodename, release, version and machine, as an os.uname_result. Unix only; platform.uname() is the portable alternative.
os.unshare
unshare(flags) / os.setns(fd, nstype=0)
Linux namespaces from Python: unshare() moves the process into new namespaces chosen by CLONE_* flags, setns() joins an existing one through a /proc/<pid>/ns file or pidfd. The building blocks of containers. Linux only, Python 3.12+.
os.urandom
urandom(size, /)
Return size random bytes from the operating system, suitable for cryptographic use. os.getrandom() is the Linux-only syscall wrapper with GRND_NONBLOCK / GRND_RANDOM flags; the secrets module is the friendly front end.
os.waitpid
waitpid(pid, options, /) / wait() / wait3(options) / wait4(pid, options) / waitid(idtype, id, options, /) / os.waitstatus_to_exitcode(status) / os.WIFEXITED(status) ... / os.pidfd_open(pid, flags=0)
Wait for a child process to finish and decode its 16-bit wait status: the exit code sits in the high byte, the killing signal in the low 7 bits. waitstatus_to_exitcode() does the decoding for you, on Unix and Windows.
LIVE
os.walk
walk(top, topdown=True, onerror=None, followlinks=False) / os.fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None)
Walk a directory tree: one (folder, sub-folder names, file names) triple per directory. Edit the sub-folder list in place to choose where it goes next.

Common patterns

Configuration from the environment
Read once at startup, convert explicitly, give a default.
import os
PORT = int(os.environ.get('PORT', '8000'))
DEBUG = os.getenv('DEBUG', '0') == '1'
Create an output folder safely
No check-then-create race, no error when it already exists.
import os
os.makedirs('build/reports', exist_ok=True)
Every file under a folder
os.walk yields one (folder, sub-folders, files) triple per directory.
import os
for root, dirs, files in os.walk('src'):
    for name in files:
        print(os.path.join(root, name))
Atomic save
Write a temp file, then os.replace it over the target — readers never see half a file.
import os
with open('state.json.tmp', 'w', encoding='utf-8') as f:
    f.write(data)
os.replace('state.json.tmp', 'state.json')

Examples

1. List a folder you just made
import os os.makedirs('data/raw', exist_ok=True) open('data/raw/a.csv', 'w').close() sorted(os.listdir('data/raw'))
Returns
['a.csv']
2. Walk a tree
import os os.makedirs('a/b/c') [(root.replace(os.sep, '/'), dirs, files) for root, dirs, files in os.walk('a')]
Returns
[('a', ['b'], []), ('a/b', ['c'], []), ('a/b/c', [], [])]
3. A default for a missing variable
import os os.getenv('SURELY_NOT_SET_12345', 'fallback')
Returns
'fallback'
4. os.path is posixpath or ntpath
import os, posixpath, ntpath os.path in (posixpath, ntpath)
Returns
True
5. mkdir makes one level, makedirs all
import os try: os.mkdir('x/y/z') except OSError as e: first = type(e).__name__ os.makedirs('x/y/z') (first, os.path.isdir('x/y/z'))
Returns
('FileNotFoundError', True)
6. Random bytes from the OS
import os len(os.urandom(16))
Returns
16

Pitfalls

1. Putting a non-string into os.environ
The environment holds strings only. Convert numbers and booleans yourself (and back again when you read them).
an int
import os
from unittest import mock
with mock.patch.dict(os.environ):
    os.environ['PORT'] = 8080
TypeError: str expected, not int
str(...)
import os
from unittest import mock
with mock.patch.dict(os.environ):
    os.environ['PORT'] = str(8080)
    port = int(os.environ['PORT'])
port
8080
2. Checking os.path.exists() before mkdir
Between the check and the mkdir another process can create the folder. exist_ok=True does both in one call.
check, then mkdir
import os
if not os.path.exists('out'):
    os.mkdir('out')
os.path.isdir('out')
True
makedirs(exist_ok=True)
import os
os.makedirs('out', exist_ok=True)
os.makedirs('out', exist_ok=True)
os.path.isdir('out')
True

When to use

Use it
  • Environment variables, the current process, low-level OS calls
  • Folder operations when you already work with str paths (listdir, walk, makedirs, remove, replace)
Reach for something else
  • Path arithmetic and reading/writing whole files → pathlib
  • Copying, moving trees, deleting non-empty folders → shutil (copy2, copytree, move, rmtree)
  • Running programs → subprocess instead of os.system / os.popen / spawn*
  • Temporary files and folders → tempfile

Notes

CPython impl
Lib/os.py — a thin layer over the posix (Unix) or nt (Windows) C module (Modules/posixmodule.c, one file for both), plus pure-Python makedirs, removedirs, renames, walk, fwalk, environ and the exec*/spawn* helpers
Availability
The docs mark each function: "Unix", "Linux", "Windows", "not WASI" … Calling a missing one is an AttributeError, so test with hasattr(os, name) when you must support several platforms
Encoding
str paths are encoded with the file system encoding (UTF-8 on Windows and in practice on Linux/macOS); bytes paths are passed through untouched

FAQ

os is the operating-system interface (environment, processes, creating and deleting files and folders). os.path is a separate module of path-string helpers — join, split, splitext, exists — imported automatically as an attribute of os. It is posixpath on Linux and macOS and ntpath on Windows.