Copy Files and Directories Recursively in Python

How to Copy Files and Directories Recursively in Python?

Python provides functions like shutil.copy for copying files and shutil.copytree for copying directories. However, I haven’t found any function that handles both files and directories in a single call. While it’s easy to check whether you’re copying a file or a directory, it still seems like an omission.

Is there no standard function in Python that works like the Unix cp -r command, meaning it supports both files and directories and copies recursively? What is the most elegant way to work around this limitation when using Python copy directory?

You can use shutil.copy and shutil.copytree in combination by writing a helper function. This lets you handle both files and directories seamlessly, based on a simple check for whether the source is a directory. Here’s how you can do it:

import shutil
import os

def copy_recursive(src, dst):
    if os.path.isdir(src):
        shutil.copytree(src, dst)
    else:
        shutil.copy(src, dst)

# Example usage:
copy_recursive('source_folder', 'destination_folder')
copy_recursive('file.txt', 'destination_file.txt')

This approach works well if you simply need to distinguish between files and directories. It’s simple and gets the job done when handling Python copy directory tasks.

Building on the previous answer, we can modernize this approach by leveraging the pathlib module, which makes handling file paths more elegant. Using Path objects simplifies the logic and improves readability:

import shutil
from pathlib import Path

def copy_recursive(src, dst):
    src = Path(src)
    dst = Path(dst)
    if src.is_dir():
        shutil.copytree(src, dst)
    else:
        shutil.copy(src, dst)

# Example usage:
copy_recursive('source_folder', 'destination_folder')
copy_recursive('file.txt', 'destination_file.txt')

pathlib integrates smoothly with modern Python codebases, and this method ensures you’re writing clean and maintainable code. It’s especially helpful for those focusing on Python copy directory and file operations in a consistent way.

While the above methods work, you might encounter scenarios where you want greater control, such as handling symlinks or copying selective content. In such cases, you can create a custom recursive function using os.walk() to replicate the functionality of Unix cp -r:

import shutil
import os

def copy_recursive(src, dst):
    if os.path.isfile(src):
        shutil.copy(src, dst)
    elif os.path.isdir(src):
        os.makedirs(dst, exist_ok=True)
        for root, dirs, files in os.walk(src):
            dst_dir = os.path.join(dst, os.path.relpath(root, src))
            os.makedirs(dst_dir, exist_ok=True)
            for file in files:
                shutil.copy(os.path.join(root, file), os.path.join(dst_dir, file))

# Example usage:
copy_recursive('source_folder', 'destination_folder')
copy_recursive('file.txt', 'destination_file.txt')

This approach mimics the recursive nature of Unix cp -r, providing flexibility to handle special cases like nested directories or permissions. It’s the most customizable solution for those needing granular control in their Python copy directory operations.