Explore the Operating System with Python `os`

Learn how to use Python's os module to work with environment variables, navigate directories, run system commands, and interact with the operating system.

7 min read

The os module in the Python standard library gives you access to operating system features like environment variables, the current working directory, file permissions, and process management. It is one of the oldest and most widely used modules in Python, and many higher-level tools like pathlib build on top of it.

pythonpython
import os
 
print(os.getcwd())               # /Users/maya/Dev/project
print(os.name)                    # posix
print(os.environ.get("HOME"))  # /Users/maya

os.getcwd() returns the current working directory as a string. os.name tells you the platform: "posix" on Linux and macOS, "nt" on Windows. os.environ is a dictionary-like object containing environment variables.

Working with environment variables

Environment variables store configuration values like API keys, database URLs, and runtime settings outside your code. Reading them through os.environ keeps secrets and per-machine settings out of your source files entirely.

pythonpython
import os
 
home = os.environ["HOME"]
path = os.environ.get("PATH", "/usr/bin")
user = os.environ.get("USER", "unknown")
 
print(home)         # /Users/maya
print(path[:50])  # /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
print(user)         # maya

os.environ["HOME"] raises a KeyError if the variable is not set. os.environ.get("PATH", "/usr/bin") returns the default value "/usr/bin" when the variable is missing. Always use .get() with a sensible default for optional variables.

You can also set environment variables for the current process:

pythonpython
import os
 
os.environ["APP_MODE"] = "testing"
print(os.environ["APP_MODE"])  # testing

This change is visible to child processes spawned by your Python program but does not affect the parent shell or other programs.

The os module provides functions for checking and changing the current working directory.

pythonpython
import os
 
print(os.getcwd())              # /Users/maya/Dev/project
os.chdir("/tmp")
print(os.getcwd())              # /tmp
os.chdir(os.environ["HOME"])
print(os.getcwd())              # /Users/maya

os.chdir(path) changes the current working directory. This affects all subsequent relative path operations in your script. It is often safer to use absolute paths with pathlib instead of changing the working directory mid-script.

Listing directory contents

os.listdir() returns the names of entries in a directory as strings.

pythonpython
import os
 
entries = os.listdir(".")
for entry in sorted(entries)[:5]:
    print(entry)

Sorting first makes the output predictable, since the raw list from os.listdir comes back in whatever order the filesystem happens to store entries:

texttext
README.md
src
tests

os.listdir(".") lists the current directory. The result is unsorted and includes both files and directories as plain names. For Path objects and more control, prefer pathlib.Path.iterdir().

To separate files from directories, combine os.listdir with os.path:

pythonpython
import os
 
entries = os.listdir(".")
files = [e for e in entries if os.path.isfile(e)]
dirs = [e for e in entries if os.path.isdir(e)]
print(f"{len(files)} files, {len(dirs)} directories")

Creating and removing directories

The os module can create single directories or nested trees in one call, which saves you from writing a loop to build out a multi-level folder structure by hand.

pythonpython
import os
 
os.makedirs("output/2026/reports", exist_ok=True)
 
os.mkdir("output/temp")
os.rmdir("output/temp")

os.makedirs("output/2026/reports", exist_ok=True) creates all three directories if they do not exist. exist_ok=True prevents an error when the path already exists.

os.mkdir creates a single directory and fails if the parent does not exist. os.rmdir removes an empty directory.

Running external commands

os.system() runs a shell command and returns its exit code. However, the official Python documentation recommends using the subprocess module instead.

pythonpython
import os
 
exit_code = os.system("echo 'Hello from the shell'")
print(f"Exit code: {exit_code}")

The command runs through the shell, prints its own output directly to the terminal, and then Python prints the numeric exit code it returned:

texttext
Hello from the shell
Exit code: 0

os.system() is simple but has serious limitations: it always passes the command through the shell (a security risk with user input), it does not capture output programmatically, and it gives you no control over timeouts or error handling.

Use subprocess.run() for all new code:

pythonpython
import subprocess
 
result = subprocess.run(
    ["echo", "Hello from subprocess"],
    capture_output=True,
    text=True,
)
print(result.stdout.strip())

Because capture_output=True redirects the child process's output back into Python instead of the terminal, the result only appears once you explicitly print it:

texttext
Hello from subprocess

subprocess.run() takes a list of arguments (no shell injection risk), captures stdout and stderr, and supports timeouts, working directories, and environment variable overrides.

Useful os.path functions

The os.path submodule provides path manipulation functions. For new code, prefer pathlib, but you will encounter os.path in existing codebases.

pythonpython
import os
 
path = "/Users/maya/Documents/report.pdf"
 
print(os.path.basename(path))                                # report.pdf
print(os.path.dirname(path))                                  # /Users/maya/Documents
print(os.path.splitext(path))                                 # ('/Users/maya/Documents/report', '.pdf')
print(os.path.exists(path))                                   # False
print(os.path.join("data", "2026", "output.csv"))  # data/2026/output.csv
os.path functionpathlib equivalent
os.path.basename(p)Path(p).name
os.path.dirname(p)Path(p).parent
os.path.splitext(p)Path(p).suffix
os.path.exists(p)Path(p).exists()
os.path.join(a, b)Path(a) / b

The pathlib versions are more readable and return Path objects instead of strings.

Practical example: inspecting the runtime environment

Combine several os features to write a script that reports information about the Python process.

pythonpython
import os
 
print("Platform:", os.name)
print("Current directory:", os.getcwd())
print("Home:", os.environ.get("HOME", "not set"))
print("Python path:", os.environ.get("PYTHONPATH", "not set"))
 
pid = os.getpid()
print(f"Process ID: {pid}")
 
files = [f for f in os.listdir(".") if os.path.isfile(f)]
print(f"Files in current directory: {len(files)}")

os.getpid() returns the process ID of the current Python interpreter. This is useful for logging, creating unique temporary file names, or debugging multi-process applications.

Common mistakes

Using os.system() for command execution. It is tempting because it is short, but os.system() is a security risk and gives you no output capture. Use subprocess.run() with a list of arguments instead.

Forgetting that os.listdir returns only names. os.listdir("data") returns ["file.txt"], not ["data/file.txt"]. You need to join with the directory path to get a usable path: os.path.join("data", name).

Changing directory with os.chdir unnecessarily. Changing the working directory affects all relative paths in your program and can cause hard-to-debug issues in larger scripts or threaded code. Use absolute paths or pathlib paths instead.

Hardcoding path separators. Never write "data\2026" or "data/2026" with hardcoded separators. Use os.path.join("data", "2026") or Path("data") / "2026" for cross-platform compatibility.

Rune AI

Rune AI

Key Insights

  • Use os.environ to read and set environment variables.
  • Use os.getcwd() and os.chdir() to work with the current working directory.
  • Use os.listdir() for a quick directory listing; prefer pathlib.Path.iterdir() for new code.
  • Use os.makedirs() with exist_ok=True to create nested directories safely.
  • Use subprocess.run() instead of os.system() for running external commands.
  • Changes to os.environ only affect the current Python process and its children.
RunePowered by Rune AI

Frequently Asked Questions

Should I use os.system() or subprocess.run() to run shell commands?

Use `subprocess.run()` for all new code. It gives you control over input, output, error handling, and timeouts. `os.system()` is simpler but less safe and less flexible. The official Python documentation recommends `subprocess` over `os.system`.

What is the difference between os.getcwd() and Path.cwd()?

Both return the current working directory. `os.getcwd()` returns a string. `Path.cwd()` from the `pathlib` module returns a Path object. For new code, prefer `pathlib` for path operations and use `os` for environment variables and process-level operations.

Can I change environment variables for other programs from Python?

No. `os.environ` changes affect only the current Python process and any child processes it spawns. You cannot modify the environment of your parent shell or other running programs from Python.

Conclusion

The os module is Python's primary interface to the operating system. Use os.environ for environment variables, os.getcwd and os.chdir for directory navigation, and os.listdir for basic directory listing. For running external commands, prefer subprocess.run over os.system. For file path operations, prefer pathlib over raw os.path functions.