Skip to content

How to Copy and Rename Files in Python

Learn how to copy and rename files in Python using shutil.copy() and shutil.copy2(), including timestamp-based filenames and recursive directory operations.

This guide explains how to copy and rename files in Python, using the shutil module. We'll cover copying and renaming a single file, creating unique filenames with timestamps, and recursively copying and renaming files within directories.

Copying and Renaming a Single File

Using shutil.copy() (Preserves Permissions)

The shutil.copy() function copies a file, preserving its permission bits. This is the most common and often the best approach.

from shutil import copy
import pathlib

src_path = pathlib.Path('example.txt')
destination_path = pathlib.Path('example-new.txt')

copy(src_path, destination_path) # Copy and rename

print('File copied and renamed successfully!')
  • You can also use strings instead of pathlib.Path objects.
  • shutil.copy(src, dst): Copies the file at src to dst. If dst is a directory, the file will be copied into that directory with the same filename. If dst is a file path, the file will be copied to that path, effectively renaming it.
  • Permissions are preserved.
  • File metadata (like creation/modification times) is not preserved.

Using shutil.copy2() (Preserves Metadata)

If you need to preserve as much file metadata as possible (creation time, modification time, etc.), use shutil.copy2():

from shutil import copy2
import pathlib

src_path = pathlib.Path('example.txt')
destination_path = pathlib.Path('example-new.txt')
copy2(src_path, destination_path)  # Copy, rename, and preserve metadata
print('File copied and renamed successfully!')
  • shutil.copy2() is identical to shutil.copy() except that it also attempts to preserve file metadata. This is useful for backups or archiving.

Creating Unique Filenames with Timestamps

To avoid overwriting existing files, a common practice is to include a timestamp in the filename:

from datetime import datetime
from shutil import copy2
import pathlib

src_path = pathlib.Path('example.txt')
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") # Format timestamp
destination_path = pathlib.Path(f'example-{timestamp}.txt')

copy2(src_path, destination_path)
print(f'File copied to {destination_path}')
  • datetime.now().strftime("%Y-%m-%d_%H-%M-%S"): Creates a timestamp string in the format "YYYY-MM-DD_HH-MM-SS". This is a good format for filenames because it's sortable and avoids problematic characters.
  • f'example-{timestamp}.txt' creates a new file.
  • Using string formatting allows us to create a filename with dynamic values.

Recursively Copying and Renaming Files in a Directory

To copy and rename all files (or files matching a specific pattern) within a directory, including files in subdirectories, use os.walk() or pathlib.Path.rglob():

import os
from shutil import copy2
from pathlib import Path

def copy_and_rename(src, dest):
    dest_path = Path(dest)
    dest_path.mkdir(parents=True, exist_ok=True) # Create destination dir

    for src_dir, _, files in os.walk(src):
        rel_path = Path(src_dir).relative_to(src)
        dest_dir = dest_path / rel_path

        dest_dir.mkdir(parents=True, exist_ok=True)  # Ensure dest dir exists

        for file in files:
            if file.endswith(".txt"):  # Only process .txt files
                src_file = Path(src_dir) / file
                file_name, extension = file.rsplit('.', 1) # Split filename
                updated_filename = file_name + '-new.' + extension
                dest_file = dest_dir / updated_filename
                copy2(src_file, dest_file)
                print(f"Copied and renamed: {src_file} -> {dest_file}")

src_path = 'my-directory'
destination = 'new-directory'

copy_and_rename(src_path, destination)
  • First, the Path object is constructed using the specified destination path.
  • Then, the code creates the destination folder if it does not exist.
  • os.walk(src): This function recursively walks through a directory tree. For each directory it encounters, it yields a tuple: (dirpath, dirnames, filenames).
    • dirpath: The path to the current directory.
    • dirnames: A list of subdirectory names in the current directory.
    • filenames: A list of filenames in the current directory.
  • Path(src_dir).relative_to(src): Calculates the relative path from the original source directory (src) to the current directory (src_dir). This is important for recreating the same directory structure in the destination.
  • dest_path / rel_path: Constructs the destination directory path by joining the base destination path with the relative path.
  • dest_dir.mkdir(parents=True, exist_ok=True): Creates the destination directory (and any parent directories if needed). exist_ok=True prevents an error if the directory already exists.
  • The code renames the .txt file using string operations, and copies it to the destination.
  • This script is more robust, handling directory creation, and uses pathlib for path manipulation, which is generally preferred over os.path for its object-oriented approach.
Navigation