Build a Simple File Manager with Python

Build a command-line file manager in Python that lists, copies, moves, renames, and deletes files, applying every file handling skill from this section in one project.

7 min read

You have spent this section learning individual file handling operations: opening files, reading from them, writing to them, appending data, working with paths, handling errors, and processing large files without running out of memory. Each skill was practiced in isolation with focused examples. A Python file manager is the project that ties all of those skills together into a single working tool, a command-line program that you can use every day to list, copy, move, rename, and delete files on your computer. Building it gives you the experience of combining path manipulation, file operations, error handling, and user interaction into a coherent program that solves a real problem.

This article walks through building that file manager step by step. You will use pathlib for all path operations, the shutil module for high-level file operations like copying and moving, and the os module for deletion. Every operation is wrapped in error handling that keeps the program running even when the user provides a bad path or a file is locked by another program. By the end, you will have a working command-line tool and a template for any future Python program that needs to manage files on disk.

Setting up the project structure

The file manager is a single Python script with a menu-driven interface. The user runs the script, sees a list of available operations, types a number or letter to choose one, and follows prompts to provide file paths. After each operation completes, the menu reappears until the user chooses to exit. This loop-based structure is the simplest way to build an interactive command-line tool, and it keeps the code organized into small, testable functions.

pythonpython
import shutil
from pathlib import Path
 
def show_menu():
    print("\nFile Manager")
    print("1. List files in a directory")
    print("2. Copy a file")
    print("3. Move a file")
    print("4. Rename a file")
    print("5. Delete a file")
    print("6. Quit")

The main function runs an infinite loop that prints the menu, reads the user's choice, and dispatches to the matching operation function. The loop exits when the user chooses option six. Any unrecognized input produces a clear error message instead of crashing:

pythonpython
def main():
    while True:
        show_menu()
        choice = input("Choose an option: ").strip()
        if choice == "6":
            print("Goodbye."); break
        elif choice in {"1", "2", "3", "4", "5"}:
            ops = {"1": list_files, "2": copy_file, "3": move_file, "4": rename_file, "5": delete_file}
            ops[choice]()
        else:
            print("Invalid choice. Enter a number from 1 to 6.")

The ops dictionary is a dispatch table. Instead of writing a long if-elif chain to call each function, main looks up the chosen function by key and calls it directly. This keeps the main loop clean and lets you add new operations by writing a new function and adding one entry to the ops dictionary.

This main function references five operation functions, but this article only walks through the code for list_files and copy_file. move_file, rename_file, and delete_file follow the same validate-confirm-execute pattern described below, so before you run main as shown, define those three functions yourself using shutil.move, path.rename, and os.remove or shutil.rmtree.

Listing files in a directory

The list_files function asks the user for a directory path and prints every file and subdirectory inside it. It uses pathlib to validate that the user provided an existing directory, and it catches common errors like FileNotFoundError when the path does not exist and NotADirectoryError when the path points to a file instead of a directory.

pythonpython
def list_files():
    path = Path(input("Directory path: ").strip())
    if not path.exists():
        print(f"Error: {path} does not exist.")
        return
    if not path.is_dir():
        print(f"Error: {path} is not a directory.")
        return
    for entry in sorted(path.iterdir()):
        kind = "DIR" if entry.is_dir() else "FILE"
        print(f"  [{kind}] {entry.name}")

The function first validates the path, returning early with a clear message if the path is missing or is not a directory. It then collects all entries, sorts them alphabetically, and prints each one with a label indicating whether it is a file or a directory. The early-return pattern for error cases keeps the happy path unindented and readable. The sorted call ensures consistent output order, and the name attribute strips the parent directory from the display so the output is clean.

Copying and moving files

Copying and moving both use shutil functions that handle the underlying file system operations correctly. The copy2 function copies a file along with its metadata, including the modification timestamp. The move function renames or relocates a file, and it works across different file systems when possible. Both operations need the source path to exist as a file and the destination to either not exist or to be a file that the user is willing to overwrite.

pythonpython
def copy_file():
    source = Path(input("Source file: ").strip())
    if not source.is_file():
        print(f"Error: {source} is not a valid file.")
        return
    destination = Path(input("Destination: ").strip())
    if destination.exists() and input(f"Overwrite {destination}? (y/n): ").lower() != "y":
        print("Copy cancelled.")
        return
    shutil.copy2(source, destination)
    print(f"Copied {source} to {destination}.")

The function validates the source, asks for confirmation before overwriting an existing destination, and only then performs the copy. This validate-confirm-execute pattern applies to every operation in the file manager. In production code, you would also wrap the shutil.copy2 call in a try-except block to catch PermissionError and other operating system errors, the same way you learned in the article on handling file errors in Python. The move_file function follows the same structure but calls shutil.move instead of shutil.copy2. The delete_file function uses os.remove for files and shutil.rmtree for directories, with extra confirmation for directories since recursive deletion is irreversible.

Putting the file manager to use

The complete file manager is around a hundred lines of code, with each operation function following the same validate-confirm-execute-catch pattern. You can extend it with additional operations: searching for files by name pattern using rglob, displaying file sizes with stat, batch renaming files using a naming template, or creating and removing directories. Each new operation is a new function hooked into the menu loop, and the pathlib and error handling patterns you have already written apply unchanged.

This project is designed to be extended. The menu loop makes it easy to add operations, and the validation pattern ensures each new operation handles bad input gracefully. If you have completed the section on Python functions, you can refactor the repeated validation logic into a shared helper function. If you have read the article on handling file errors in Python, the try-except patterns will feel familiar. And if you want to take the file manager further, you can add a configuration file for user preferences, a log file that records every operation, or even a simple graphical interface using Python's built-in tkinter module.

The file manager is the capstone of this section because it requires every file handling skill in combination. You open, read, and write files. You construct and validate paths. You choose between write and append modes for log files. You handle errors without crashing. You process directory listings one entry at a time. And you build a tool that you can actually use, a program that manages real files on your real computer, written in Python that you understand from top to bottom.

Rune AI

Rune AI

Key Insights

  • pathlib provides Path objects that make path manipulation clean and cross-platform.
  • shutil.copy2 copies a file along with its metadata, and shutil.move renames or relocates.
  • Always validate paths with exists() and is_file() before performing operations on them.
  • Wrap file operations in try-except to handle missing files, permissions errors, and other failures gracefully.
  • A command-line menu loop is a simple but effective way to structure an interactive Python tool.
RunePowered by Rune AI

Frequently Asked Questions

What Python modules do I need to build a file manager?

The standard library provides everything you need: pathlib for path handling, shutil for high-level file operations like copying and moving, and os for lower-level operations like deleting files. No third-party packages are required for a basic file manager.

How do I make my file manager safe so it does not accidentally delete important files?

Always check that a path exists and is the expected type before operating on it. Use path.exists() and path.is_file() or path.is_dir() to validate paths. For destructive operations like deleting files, print what you are about to do and ask for confirmation before executing. A dry-run mode that shows what would happen without making changes is also a good practice.

Can I use this file manager as a starting point for a larger project?

Yes. The patterns demonstrated here, path validation, error handling, user confirmation for destructive operations, and modular function design, are the same patterns used in production-quality file management tools. You can extend this project with features like recursive directory operations, file searching by pattern, batch renaming, or a graphical interface.

Conclusion

Building a Python file manager brings together every skill from this section: opening and closing files safely, working with paths, choosing the right file modes, handling errors without crashing, and processing operations one at a time. The project is small enough to complete in an afternoon but real enough that you will use the same patterns in every file-heavy Python program you write afterward. Extend it with search, batch operations, or a GUI as your skills grow.