Skip to content

How to Copy File Contents in Python

How to copy file contents in Python using shutil.copy2(), file objects, and chunked reading for large files, with examples for preserving metadata and working with file-like objects.

Copying file contents efficiently is essential for backups, data migration, and file processing. Python provides multiple approaches ranging from simple one-liners to fine-grained control over the copying process.

The shutil module provides optimized file copying using OS-level system calls for best performance.

import shutil

shutil.copyfile("source.txt", "destination.txt")

shutil.copy2("source.txt", "backup.txt")

print("File copied successfully")

Compare shutil Copy Functions

import shutil
import os

source = "document.txt"

shutil.copyfile(source, "copy1.txt")

shutil.copy(source, "./backups/")  # Creates backups/document.txt

shutil.copy2(source, "copy2.txt")

original_stat = os.stat(source)
copy2_stat = os.stat("copy2.txt")

print(f"Original modified time: {original_stat.st_mtime}")
print(f"copy2 modified time:    {copy2_stat.st_mtime}")  # Same
Function Content Permissions Timestamps Dest Can Be Dir
copyfile() ✅ ❌ ❌ ❌
copy() ✅ ✅ ❌ ✅
copy2() ✅ ✅ ✅ ✅

Copy Large Files with Chunked Reading

For files larger than available memory, or when you need to process data during copying, read in chunks.

source = "large_video.mp4"
destination = "copy.mp4"

buffer_size = 1024 * 1024

with open(source, 'rb') as f_in, open(destination, 'wb') as f_out:
    while chunk := f_in.read(buffer_size):
        f_out.write(chunk)

print("Large file copied successfully")

Copy with Progress Tracking

import os

def copy_with_progress(source: str, destination: str, buffer_size: int = 1024 * 1024):
    """Copy file with progress percentage display."""
    file_size = os.path.getsize(source)
    copied = 0
    
    with open(source, 'rb') as f_in, open(destination, 'wb') as f_out:
        while chunk := f_in.read(buffer_size):
            f_out.write(chunk)
            copied += len(chunk)
            progress = (copied / file_size) * 100
            print(f"\rProgress: {progress:.1f}%", end="")
    
    print("\nCopy complete!")

copy_with_progress("large_file.zip", "backup.zip")

Copy Between Open File Objects

Use copyfileobj() when working with already-open file handles or file-like objects.

import shutil

with open('source.txt', 'rb') as src:
    with open('destination.txt', 'wb') as dst:
        shutil.copyfileobj(src, dst)

Specify Buffer Size

import shutil

with open('large_file.bin', 'rb') as src:
    with open('copy.bin', 'wb') as dst:
        shutil.copyfileobj(src, dst, length=65536)

Append File Contents

Merge one file into another by opening in append mode.

import shutil

with open('log_archive.txt', 'a') as archive:
    with open('today.log', 'r') as today:
        shutil.copyfileobj(today, archive)

print("Log appended to archive")

Append Multiple Files

import shutil
from pathlib import Path

log_files = sorted(Path('./logs').glob('*.log'))

with open('combined.log', 'w') as combined:
    for log_file in log_files:
        combined.write(f"\n=== {log_file.name} ===\n")
        with open(log_file, 'r') as f:
            shutil.copyfileobj(f, combined)

print(f"Combined {len(log_files)} log files")

Copy with Error Handling

Implement robust copying with proper error handling.

import shutil
from pathlib import Path

def safe_copy(source: str, destination: str) -> bool:
    """Copy file with comprehensive error handling."""
    source_path = Path(source)
    dest_path = Path(destination)
    
    if not source_path.exists():
        print(f"Error: Source file '{source}' not found")
        return False
    
    dest_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        shutil.copy2(source, destination)
        print(f"Copied: {source} -> {destination}")
        return True
    except PermissionError:
        print(f"Error: Permission denied for '{destination}'")
        return False
    except shutil.SameFileError:
        print(f"Error: Source and destination are the same file")
        return False
    except OSError as e:
        print(f"Error: {e}")
        return False

safe_copy("data.txt", "./backup/data.txt")

Copy Text Files with Encoding Control

For text files requiring encoding conversion or line ending normalization.

def copy_with_encoding(source: str, destination: str, 
                       source_encoding: str = 'utf-8',
                       dest_encoding: str = 'utf-8'):
    """Copy text file with encoding conversion."""
    with open(source, 'r', encoding=source_encoding) as f_in:
        content = f_in.read()
    
    with open(destination, 'w', encoding=dest_encoding) as f_out:
        f_out.write(content)

copy_with_encoding('old_file.txt', 'new_file.txt', 
                   source_encoding='latin-1', 
                   dest_encoding='utf-8')

Normalize Line Endings

def copy_normalize_newlines(source: str, destination: str):
    """Copy text file, converting all line endings to Unix style."""
    with open(source, 'r', newline=None) as f_in:  # Universal newline mode
        content = f_in.read()
    
    with open(destination, 'w', newline='\n') as f_out:
        f_out.write(content)

copy_normalize_newlines('windows_file.txt', 'unix_file.txt')

Quick Reference

Task Method Notes
Fast copy, content only shutil.copyfile(src, dst) Destination must be file path
Copy with metadata shutil.copy2(src, dst) Best for backups
Copy open file handles shutil.copyfileobj(f_in, f_out) Works with file-like objects
Large files with control Manual chunked reading Progress tracking, processing
Append to file copyfileobj with 'a' mode Merge files

Conclusion

Use shutil.copy2() for general file copying. It's optimized, handles edge cases, and preserves file metadata. For large files requiring progress tracking or custom processing, implement chunked reading with a buffer. Use copyfileobj() when working with open file handles or file-like objects such as network streams. Always use binary mode ('rb', 'wb') for non-text files to prevent data corruption.

Navigation