Python os module – 10 Must-Know Functions

I write a lot of small Python utilities that peek at the filesystem, read env vars, or spin up processes. Every time, I reach for the same module first — the os module from the standard library. It has been around forever, covers all three major platforms without changes, and genuinely makes scripts more readable than if I tried wrapping raw system calls myself. After using it for years I still find functions I underused, so let me walk through the ten I reach for most.

This article covers filesystem navigation, directory listing, file and folder creation and deletion, path construction, environment variable reading, and running shell commands. By the end you will know exactly which function reaches for for common automation tasks.

Python os module import

TLDR

  • os.getcwd() and os.chdir() for filesystem navigation
  • os.listdir() for reading directory contents
  • os.mkdir(), os.makedirs(), os.rmdir(), and os.remove() for file and directory operations
  • os.path.join(), os.path.exists(), os.path.isfile(), and os.path.isdir() for path handling
  • os.getenv() and os.system() for environment access and shell commands

What is the os module?

The os module in Python provides a portable interface to the operating system. It wraps low-level system calls and exposes them as Python functions that behave the same way regardless of which platform runs your code. The os module ships with every Python installation, so you never need to install anything separately.

The module also includes the os.path submodule for path construction and inspection — that submodule shows up constantly in real code, so understanding it well pays off quickly. With that foundation in place, let me move into the specific functions.

Navigating the File System

The first thing most scripts need to know is where they are running. The os module gives you two functions that handle this cleanly.

os.getcwd()

The getcwd() function returns the current working directory as a string. The name stands for “get current working directory”. I use this when scripts need to know their own location or construct relative paths.


import os

cwd = os.getcwd()
print(cwd)

Build scripts often need this because code frequently references files relative to where the script lives. Calling getcwd() gives you a reliable anchor point to resolve those relative references from.


/home/ubuntu/.hermes/hermes-agent

os.chdir()

The chdir() function changes the current working directory to a path you provide. I use this whenever a script needs to hop into a project folder before reading or writing files, similar to how built-in methods work in other contexts.


import os

print("Before:", os.getcwd())
os.chdir('/tmp')
print("After:", os.getcwd())

The function raises FileNotFoundError if the target does not exist. Pair this with os.path.exists() when building paths dynamically to avoid surprises.


Before: /home/ubuntu/.hermes/hermes-agent
After: /tmp

Once you know where you are and can move around, the natural next step is seeing what lives in a directory.

Listing Directory Contents

The os.listdir() function returns a list of names inside a given directory. Call it with no arguments to use the current working directory.


import os

entries = os.listdir('/tmp')
print(entries[:5], "...")

The function gives plain filenames, not full paths. Combine it with os.path.join() when you need complete path strings for further processing.


['.font-unix', '.ICE-unix', '.Test-unix', '.X11-unix', '.osquery_temp_k不存在'] ...


import os

for name in os.listdir('/tmp')[:3]:
    full_path = os.path.join('/tmp', name)
    print(full_path)

This pattern — listing then joining — is the foundation of most recursive file walkers. The combination comes up so often that it is worth memorising.


/tmp/.font-unix
/tmp/.ICE-unix
/tmp/.Test-unix

Reading directory contents leads naturally into creating and removing things. The os module gives you a complete set of functions for that too.

Creating and Removing Directories and Files

os.mkdir()

The mkdir() function creates a single directory at the specified path. It creates only one level, so the parent must already exist. If you try to create a nested path in one call, it raises an error.


import os

os.mkdir('/tmp/test_article_dir')
print(os.path.exists('/tmp/test_article_dir'))

This makes mkdir() best suited for situations where you already know the parent structure exists, such as within a project you control.


True

os.makedirs()

The makedirs() function creates all intermediate directories in a path, not just the final one. I prefer this when I need to set up a nested folder structure and I am not sure whether parent directories exist.


import os

os.makedirs('/tmp/test_article_dir/nested/deep')
print(os.path.exists('/tmp/test_article_dir/nested/deep'))

.makedirs() handles the case where the full chain does not yet exist, creating every directory in the path as needed. That convenience comes with one caveat — it will also create any missing intermediate directories, which might mask typos in directory names during development.


True

os.rmdir()

The rmdir() function removes an empty directory. It fails if the directory contains any files or subdirectories, which protects you from accidental data loss.


import os

os.rmdir('/tmp/test_article_dir')
print(os.path.exists('/tmp/test_article_dir'))

This guardrail makes rmdir() safe for cleanup scripts where you want to remove a folder you know should be empty, without the risk of wiping something that still has content.


False

os.remove()

The remove() function deletes a single file from the filesystem. I use this when my script needs to clean up temporary files or remove output files during a processing pipeline.


import os

with open('/tmp/test_file.txt', 'w') as f:
    f.write('temporary content')

print(os.path.exists('/tmp/test_file.txt'))
os.remove('/tmp/test_file.txt')
print(os.path.exists('/tmp/test_file.txt'))

Note that remove() only works on files — calling it on a directory raises a PermissionError. For deleting directories with contents, shutil.rmtree() is the appropriate tool, but that lives in a different module.


True
False

Creating and deleting paths is only half the story — the other half is constructing paths correctly in the first place, which is where the os.path submodule comes in.

Working with File Paths

os.path.join()

The join() function combines path components using the correct separator for your operating system. I always use this instead of string concatenation because it handles edge cases automatically.


import os

print(os.path.join('home', 'user', 'docs'))
print(os.path.join('/home', 'user', 'docs'))

On Linux and macOS this produces forward-slash paths; on Windows it uses backslashes. Using join() means your code works correctly across all platforms without any conditional logic.


home/user/docs
/home/user/docs

os.path.exists()

The exists() function returns True if the given path points to something on disk, and False otherwise. I use this to verify that a directory or file is present before attempting to read from it or write to it.


import os

print(os.path.exists('/tmp'))
print(os.path.exists('/tmp/nonexistent'))

exists() is the simplest form of guard you can place before file operations. It costs almost nothing and prevents the more cryptic errors that arise when code tries to open a missing file.


True
False

os.path.isfile() and os.path.isdir()

These two functions distinguish between files and directories. The isfile() returns True only for regular files, while isdir() returns True only for directories. I use them together to validate input paths before my script proceeds.


import os

print("isfile('/etc/passwd'):", os.path.isfile('/etc/passwd'))
print("isdir('/etc/passwd'):", os.path.isdir('/etc/passwd'))
print("isdir('/tmp'):", os.path.isdir('/tmp'))

The key thing to remember is that isfile() returns False for directories and isdir() returns False for regular files — they are not strict opposites since both return False for symlinks and non-existent paths. For robust path validation, check both when the path type matters for your logic.


isfile('/etc/passwd'): True
isdir('/etc/passwd'): False
isdir('/tmp'): True

Beyond the filesystem, the os module also connects to environment variables and the shell — two areas that come up constantly in deployment and build scripts.

Environment Variables and System Commands

os.getenv()

The getenv() function retrieves the value of an environment variable. If the variable does not exist, it returns None by default, although you can provide a second argument as a fallback.


import os

print(os.getenv('HOME'))
print(os.getenv('USER'))

Reading environment variables is the standard way to pass configuration into Python scripts — things like API keys, feature flags, and deployment context typically come through env vars rather than hardcoded values.


/home/ubuntu
ubuntu

When the variable does not exist, a default value serves as a fallback. This keeps scripts from crashing when an expected variable is not set.


import os

result = os.getenv('NONEXISTENT_VAR', 'default_value')
print(result)


default_value

os.system()

The system() function runs a command string in the system shell and returns the exit code. I use this for quick operations where capturing output is not needed.


import os

result = os.system('echo "Hello from os.system"')
print("Exit code:", result)

system() is straightforward but limited — it runs the command through the shell, captures only the exit code, and gives you no access to stdout or stdin streams. For anything that needs output capture or fine-grained argument control, subprocess.run() is the better choice and belongs in a different module.


Hello from os.system
Exit code: 0

FAQ

When should os.path.join() be used instead of string concatenation?

The os.path.join() function is preferred because it automatically handles operating system separators and avoids edge cases like double slashes. String concatenation works on one platform but breaks when code runs on a different operating system.

How does os.getenv() differ from os.environ?

The os.getenv() function retrieves a single variable by name and supports a default fallback value. The os.environ object acts like a dictionary and provides access to all environment variables at once. For reading one specific variable, getenv() tends to produce cleaner code.

Can os.rmdir() remove a directory that contains files?

No, os.rmdir() only removes empty directories. Attempting to use it on a directory with contents raises an OSError. Use shutil.rmtree() when deletion of a directory and everything inside it is required.

What happens when os.chdir() receives a path that does not exist?

Python raises a FileNotFoundError exception and the working directory remains unchanged. Checking the path with os.path.exists() before calling chdir() helps prevent unexpected errors in scripts that build paths dynamically.

Is os.system() safe to use with user-provided input?

Running os.system() with unvalidated user input can create security vulnerabilities, especially when the input contains shell metacharacters. Consider using subprocess.run() with proper argument handling for any scenario where untrusted data might reach the command string.

Safa Mulani
Safa Mulani

An enthusiast in every forthcoming wonders!

Articles: 189