The os module provides portable ways to use operating system functionality. Here are the patterns that matter.
Environment Variables
import os
# Get with default
debug = os.environ.get("DEBUG", "false")
api_key = os.getenv("API_KEY", "") # Same as environ.get
# Check existence
if "HOME" in os.environ:
print(f"Home: {os.environ['HOME']}")
# Set temporarily
os.environ["MY_VAR"] = "value"
del os.environ["MY_VAR"]
# Expand variables in strings
path = os.path.expandvars("$HOME/documents")Path Operations
# Join paths (use pathlib for new code, but os.path is everywhere)
full_path = os.path.join("dir", "subdir", "file.txt")
# Split components
dirname = os.path.dirname("/home/user/file.txt") # /home/user
basename = os.path.basename("/home/user/file.txt") # file.txt
root, ext = os.path.splitext("file.tar.gz") # ('file.tar', '.gz')
# Normalize paths
clean = os.path.normpath("dir/../other/./file") # other/file
absolute = os.path.abspath("relative/path") # Full path
# Check path types
os.path.exists("/some/path")
os.path.isfile("/some/path")
os.path.isdir("/some/path")
os.path.islink("/some/path")Directory Operations
# Current directory
cwd = os.getcwd()
os.chdir("/new/directory")
# Create directories
os.mkdir("single_dir") # Single level
os.makedirs("nested/path/dirs", exist_ok=True) # Create parents
# Remove directories
os.rmdir("empty_dir") # Must be empty
os.removedirs("nested/empty/dirs") # Remove empty parents too
# List contents
entries = os.listdir(".") # List of namesFile Operations
# Remove files
os.remove("file.txt")
os.unlink("file.txt") # Same thing
# Rename/move
os.rename("old.txt", "new.txt")
os.replace("old.txt", "new.txt") # Atomic, cross-filesystem
# File stats
stat_result = os.stat("file.txt")
print(stat_result.st_size) # Size in bytes
print(stat_result.st_mtime) # Modification time
# Permissions
os.chmod("file.txt", 0o755)
os.chown("file.txt", uid, gid) # Unix onlyWalking Directories
# Classic pattern
for root, dirs, files in os.walk("."):
# Skip hidden directories
dirs[:] = [d for d in dirs if not d.startswith(".")]
for file in files:
full_path = os.path.join(root, file)
print(full_path)
# Bottom-up (for deletions)
for root, dirs, files in os.walk(".", topdown=False):
for file in files:
os.remove(os.path.join(root, file))
for d in dirs:
os.rmdir(os.path.join(root, d))Process Information
# Current process
print(os.getpid()) # Process ID
print(os.getppid()) # Parent process ID (Unix)
print(os.getuid()) # User ID (Unix)
print(os.getgid()) # Group ID (Unix)
# CPU info
print(os.cpu_count()) # Number of CPUs
# System name
print(os.name) # 'posix', 'nt', 'java'
print(os.uname()) # Detailed system info (Unix)Executing Commands
# Simple command (avoid for new code - use subprocess)
exit_code = os.system("ls -la")
# Better: get output
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)
# Environment for subprocess
env = os.environ.copy()
env["MY_VAR"] = "value"
subprocess.run(["command"], env=env)File Descriptors
# Low-level operations (rarely needed)
fd = os.open("file.txt", os.O_RDONLY)
try:
data = os.read(fd, 1024)
finally:
os.close(fd)
# Duplicate file descriptors
new_fd = os.dup(fd)
# Redirect stdout
saved = os.dup(1)
os.dup2(new_fd, 1)
# ... operations go to new_fd ...
os.dup2(saved, 1) # RestoreTemporary Files
# Get temp directory
tmp = os.environ.get("TMPDIR", "/tmp")
# Or use tempfile module (preferred)
import tempfile
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b"data")
temp_path = f.nameSymbolic Links
# Create symlink
os.symlink("target.txt", "link.txt")
# Read link target
target = os.readlink("link.txt")
# Check if symlink (doesn't follow)
os.path.islink("link.txt") # True
os.path.exists("link.txt") # True if target exists
# Stat without following
os.lstat("link.txt")Platform Detection
import os
import sys
# Basic platform
if os.name == "nt":
print("Windows")
elif os.name == "posix":
print("Unix-like")
# More specific
if sys.platform == "darwin":
print("macOS")
elif sys.platform.startswith("linux"):
print("Linux")
elif sys.platform == "win32":
print("Windows")Common Recipes
def ensure_dir(path):
"""Create directory if it doesn't exist."""
os.makedirs(path, exist_ok=True)
def safe_delete(path):
"""Delete file if it exists."""
try:
os.remove(path)
except FileNotFoundError:
pass
def find_files(directory, extension):
"""Find all files with given extension."""
results = []
for root, _, files in os.walk(directory):
for f in files:
if f.endswith(extension):
results.append(os.path.join(root, f))
return results
def get_size(path):
"""Get total size of file or directory."""
if os.path.isfile(path):
return os.path.getsize(path)
total = 0
for root, _, files in os.walk(path):
for f in files:
total += os.path.getsize(os.path.join(root, f))
return totalAccess Checks
# Check permissions before trying
if os.access("file.txt", os.R_OK):
# Can read
pass
if os.access("file.txt", os.W_OK):
# Can write
pass
if os.access("/usr/bin/python", os.X_OK):
# Can execute
pass
# Combined check
if os.access("file.txt", os.R_OK | os.W_OK):
# Can read and write
passFor new code, prefer pathlib for path operations and subprocess for running commands. But os remains essential for environment variables, process info, and low-level operations.
React to this post: