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.
Copy Files with shutil (Recommended)
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
| Function | Content | Permissions | Timestamps | Dest Can Be Dir |
|---|---|---|---|---|
copyfile() | ✅ | ❌ | ❌ | ❌ |
copy() | ✅ | ✅ | ❌ | ✅ |
copy2() | ✅ | ✅ | ✅ | ✅ |
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')
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
| 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.