Skip to main content

How to Copy File Contents in Python

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

# Copy content only (fastest)
shutil.copyfile("source.txt", "destination.txt")

# Copy content + preserve metadata (timestamps, permissions)
shutil.copy2("source.txt", "backup.txt")

print("File copied successfully")

Compare shutil Copy Functions

import shutil
import os

source = "document.txt"

# copyfile: Content only, destination must be a file path
shutil.copyfile(source, "copy1.txt")

# copy: Content + permissions, destination can be a directory
shutil.copy(source, "./backups/") # Creates backups/document.txt

# copy2: Content + all metadata (timestamps, permissions)
shutil.copy2(source, "copy2.txt")

# Verify metadata preservation
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
FunctionContentPermissionsTimestampsDest Can Be Dir
copyfile()
copy()
copy2()
tip

Use copy2() for backups where preserving file metadata matters. Use copyfile() when you only need the content and want maximum speed.

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"

# 1 MB buffer size
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:
# Copies efficiently using internal buffering
shutil.copyfileobj(src, dst)

Specify Buffer Size

import shutil

with open('large_file.bin', 'rb') as src:
with open('copy.bin', 'wb') as dst:
# Use 64KB buffer for better performance with large files
shutil.copyfileobj(src, dst, length=65536)

Append File Contents

Merge one file into another by opening in append mode.

import shutil

# Append today's log to the archive
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:
# Add separator between 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)

# Validate source exists
if not source_path.exists():
print(f"Error: Source file '{source}' not found")
return False

# Create destination directory if needed
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

# Usage
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)

# Convert legacy file from Latin-1 to UTF-8
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')
warning

Only use text mode copying for actual text files. Binary files (images, videos, executables) must use binary mode ('rb' and 'wb') to prevent corruption.

Quick Reference

TaskMethodNotes
Fast copy, content onlyshutil.copyfile(src, dst)Destination must be file path
Copy with metadatashutil.copy2(src, dst)Best for backups
Copy open file handlesshutil.copyfileobj(f_in, f_out)Works with file-like objects
Large files with controlManual chunked readingProgress tracking, processing
Append to filecopyfileobj with 'a' modeMerge 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.