Python programs often need to interact with the operating system. A script may create folders, list files, rename reports, read environment variables, or organize downloads. The Python os module provides standard tools for these tasks without requiring an external package.
This guide explains the most useful parts of os and os.path for beginners. You will learn how to inspect the current directory, create nested folders, join paths safely, rename and delete files, walk through directory trees, read environment variables, and build a practical file organizer.
For newer object-oriented path handling, also read the companion guide to Python pathlib. The two modules can work together: os remains important for environment variables, process information, permissions, and many operating-system operations, while pathlib often produces clearer path code.
What Is the Python os Module?
os is part of Python’s standard library. It exposes portable operating-system interfaces so the same program can perform many file and directory tasks on Windows, macOS, and Linux.
import os
print(os.name)
print(os.getcwd())os.name gives a broad platform identifier such as "nt" on Windows or "posix" on Unix-like systems. For detailed platform information, use the separate platform module.
The official Python os documentation lists the full API. The related os.path reference covers pathname operations.
Find and Change the Current Working Directory
The current working directory is the folder Python uses to resolve relative paths.
import os
current_directory = os.getcwd()
print(current_directory)
os.chdir("data")
print(os.getcwd())os.chdir() changes the process’s working directory. Use it carefully because every later relative path will be interpreted from the new location. In many scripts, keeping the working directory unchanged and building explicit paths is easier to reason about.
Path confusion is a common cause of missing-file errors. The guide to fixing Python FileNotFoundError explains how to diagnose those cases.
List Files and Folders
os.listdir() returns the names inside a directory:
import os
for name in os.listdir("."):
print(name)The result includes files and subdirectories but not their full paths. Join each name to the parent directory before inspecting it.
base_directory = "reports"
for name in os.listdir(base_directory):
full_path = os.path.join(base_directory, name)
if os.path.isfile(full_path):
print("File:", full_path)
elif os.path.isdir(full_path):
print("Folder:", full_path)Join Paths Safely with os.path.join()
Do not build filesystem paths by manually adding slashes. Different operating systems use different separators, and duplicate separators are easy to introduce.
folder = "exports"
filename = "sales.csv"
path = os.path.join(folder, filename)
print(path)os.path.join() uses the correct separator for the current platform. The companion guide on pathlib paths shows the modern alternative: Path("exports") / "sales.csv".
Create Directories
os.mkdir() creates one folder. Its parent must already exist.
os.mkdir("output")os.makedirs() can create an entire nested structure:
os.makedirs(
os.path.join("project", "logs", "archive"),
exist_ok=True,
)exist_ok=True prevents an error when the final directory already exists. It does not hide every possible failure: permissions and invalid paths can still raise exceptions.
Rename and Move Files
os.rename() can rename an item or move it to another location on the same filesystem.
old_path = os.path.join("reports", "draft.csv")
new_path = os.path.join("reports", "sales-2026.csv")
os.rename(old_path, new_path)When moving files between different disks or filesystems, shutil.move() is usually more flexible. See the guide to copying and moving files with shutil for complete examples.
Delete Files and Empty Folders
file_path = os.path.join("temp", "old-report.txt")
if os.path.isfile(file_path):
os.remove(file_path)Use os.rmdir() to remove an empty directory:
empty_folder = os.path.join("temp", "empty")
if os.path.isdir(empty_folder):
os.rmdir(empty_folder)These operations normally bypass the desktop recycle bin. Confirm the path before deleting anything, especially when the value comes from user input or configuration. To remove a directory tree, use shutil.rmtree() only after adding strong safeguards.
Inspect Path Components
path = os.path.join("data", "archive", "customers.csv")
print(os.path.basename(path)) # customers.csv
print(os.path.dirname(path)) # data/archive
print(os.path.splitext(path)) # (.../customers, .csv)
print(os.path.abspath(path))splitext() separates the final extension from the rest of the path. It is useful for classifying files by type, but remember that a suffix is only a naming convention; it does not prove the actual file format.
Check Whether a Path Exists
path = os.path.join("config", "settings.json")
if os.path.exists(path):
print("The path exists")
if os.path.isfile(path):
print("It is a file")
if os.path.isdir(path):
print("It is a directory")Existence checks can help produce friendly messages, but they do not replace exception handling. Another process may change the file after your check. Open or modify the resource inside an appropriate try/except block when reliability matters.
Walk Through a Directory Tree
os.walk() recursively visits a directory and yields the current folder, its subfolder names, and its filenames.
for current_folder, subfolders, filenames in os.walk("project"):
print("Folder:", current_folder)
for filename in filenames:
file_path = os.path.join(current_folder, filename)
print(" ", file_path)This is useful for backups, storage reports, code searches, and bulk conversion. For recurring backup work, the automatic Python backup guide provides a larger workflow.
Read and Set Environment Variables
Environment variables store configuration outside source code. They are commonly used for modes, service addresses, and secret identifiers.
import os
environment = os.getenv("APP_ENV", "development")
api_url = os.getenv("API_URL")
print(environment)
print(api_url)os.getenv() returns None or a supplied default when the variable is missing. You can also access os.environ like a mapping:
os.environ["APP_MODE"] = "test"
print(os.environ["APP_MODE"])A value assigned this way affects the current process and child processes, not the user’s permanent system configuration. Never print secrets or commit them to source control. The guide to reading environment variables safely covers validation and .env files.
Get File Metadata
path = os.path.join("reports", "sales.csv")
metadata = os.stat(path)
print("Size:", metadata.st_size)
print("Modified timestamp:", metadata.st_mtime)os.stat() returns metadata such as size, timestamps, and permission information. Timestamp values are seconds since the platform epoch; use datetime to convert them into readable dates.
Practical Project: Organize Files by Extension
The following script scans one directory and moves files into category folders. It uses os for inspection and directory creation, and shutil.move() for robust moves.
import os
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 category_for(filename):
extension = os.path.splitext(filename)[1].lower()
for category, extensions in CATEGORIES.items():
if extension in extensions:
return category
return "other"
def unique_destination(folder, filename):
stem, extension = os.path.splitext(filename)
candidate = os.path.join(folder, filename)
number = 1
while os.path.exists(candidate):
candidate = os.path.join(
folder,
f"{stem}-{number}{extension}",
)
number += 1
return candidate
def organize_files(directory):
directory = os.path.abspath(directory)
if not os.path.isdir(directory):
raise NotADirectoryError(directory)
moved = 0
for filename in os.listdir(directory):
source = os.path.join(directory, filename)
if not os.path.isfile(source):
continue
category = category_for(filename)
destination_folder = os.path.join(directory, category)
os.makedirs(destination_folder, exist_ok=True)
destination = unique_destination(
destination_folder,
filename,
)
shutil.move(source, destination)
moved += 1
print(f"Moved {filename} to {category}/")
return moved
if __name__ == "__main__":
total = organize_files("downloads-test")
print(f"Finished: {total} files moved")The function avoids overwriting an existing file by adding a number to the destination name. For unattended use, replace or supplement print() with Python logging.
os vs. pathlib vs. shutil
| Module | Best suited for |
|---|---|
os | Environment variables, process and platform operations, directory functions, low-level OS interfaces. |
os.path | String-based path inspection and manipulation. |
pathlib | Readable, object-oriented path and file operations. |
shutil | High-level copying, moving, archiving, and directory-tree operations. |
You do not have to choose only one. Real applications often use all three where each is clearest.
Common Mistakes
- Concatenating paths with hard-coded slashes.
- Assuming the working directory is the script directory.
- Deleting a path without confirming what it points to.
- Ignoring permissions and file-lock errors.
- Using
os.rename()for cross-filesystem moves without a fallback. - Logging environment-variable secrets.
- Checking existence but not handling a later operation failure.
Frequently Asked Questions
Do I need to install os?
No. It is included in Python’s standard library.
How do I get the script’s directory?
Use os.path.dirname(os.path.abspath(__file__)). In interactive environments, __file__ may not exist.
How do I create nested folders?
Use os.makedirs(path, exist_ok=True).
Can os move a file?
os.rename() can move or rename files in supported situations. Use shutil.move() for a higher-level solution, especially across filesystems.
Is pathlib replacing os?
No. Pathlib replaces many string-based path tasks, but os still exposes operating-system features that pathlib does not.
Conclusion
The Python os module is a foundational tool for automation and system interaction. Learn getcwd(), listdir(), makedirs(), os.path.join(), environment variables, and safe deletion first. Then add os.walk(), metadata, permissions, and process features as your projects require them.
Use explicit paths, validate destructive operations, and handle exceptions around real filesystem work. Those habits make scripts safer and easier to run on different computers.






