How to use shutil.copy() Method in Python

When I was working on a project where I needed to copy customer data files from one directory to another for backup purposes. The challenge was finding an efficient way to handle file copying operations in Python.

That’s when I discovered the power of Python’s shutil.copy() method. This built-in function makes file copying operations incredibly easy and reliable.

In this comprehensive guide, I’ll show you everything you need to know about using shutil.copy() method in Python. From basic file copying to advanced techniques, we’ll cover it all with practical, real-world examples.

What is shutil.copy() Method in Python?

The shutil.copy() method is part of Python’s built-in shutil module (short for “shell utilities”). It provides a high-level interface for file operations, particularly copying files and directories.

Think of shutil.copy() as your reliable assistant for file management tasks. It handles the complexity of file operations behind the scenes, making your code cleaner and more maintainable.

The method copies both the file content and the file’s permission bits, but it doesn’t preserve metadata like creation time and modification time.

Basic Syntax of shutil.copy()

Here’s the basic syntax of the shutil.copy() method:

shutil.copy(src, dst)
  • src: Source file path (the file you want to copy)
  • dst: Destination path (where you want to copy the file)

The method returns the path to the newly created file.

Methods to Use shutil.copy() method in Python

Let me show you some easy methods to use shutil.copy() method in Python.

Method 1 – Basic File Copying with shutil.copy()

Let me show you the simple way to copy files using shutil.copy() in Python. This is the method I use most frequently in my daily projects.

import shutil
import os

# Example: Copying a sales report file
source_file = "C:/Users/John/Documents/sales_report_2024.xlsx"
destination_file = "C:/Users/John/Backup/sales_report_backup.xlsx"

try:
    # Copy the file
    copied_file = shutil.copy(source_file, destination_file)
    print(f"File successfully copied to: {copied_file}")
except FileNotFoundError:
    print("Source file not found. Please check the file path.")
except PermissionError:
    print("Permission denied. Check if you have write access to the destination.")
except Exception as e:
    print(f"An error occurred: {e}")

I executed the above example code and added the screenshot below.

python copy file

This basic approach works perfectly for most file copying scenarios. I always wrap the operation in a try-except block to handle potential errors gracefully.

Method 2 – Copy Files to a Directory

Sometimes you want to copy a file to a directory without specifying the exact filename. The Python’s shutil.copy() method handles this elegantly.

import shutil
import os

# Example: Copying employee database to backup folder
source_file = "C:/Company/Data/employee_database.db"
destination_directory = "C:/Company/Backup/"

# Ensure the destination directory exists
os.makedirs(destination_directory, exist_ok=True)

try:
    # Copy file to directory (keeps original filename)
    copied_file = shutil.copy(source_file, destination_directory)
    print(f"Database backed up to: {copied_file}")

    # Verify the file was copied
    if os.path.exists(copied_file):
        file_size = os.path.getsize(copied_file)
        print(f"Copied file size: {file_size} bytes")

except Exception as e:
    print(f"Backup failed: {e}")

I executed the above example code and added the screenshot below.

shutil copy

When you specify a directory as the destination, shutil.copy() automatically uses the original filename. This is incredibly useful for backup operations.

Method 3 – Use shutil.copy2() for Metadata Preservation

While working on archival systems, I discovered that sometimes you need to preserve file metadata. That’s where shutil.copy2() comes in handy.

import shutil
import os
from datetime import datetime

# Example: Archiving legal documents with metadata
source_file = "C:/LegalDocs/contract_2024.pdf"
archive_directory = "C:/Archive/Contracts/"

# Create archive directory if it doesn't exist
os.makedirs(archive_directory, exist_ok=True)

def archive_document(source, destination_dir):
    try:
        # Get file info before copying
        original_mtime = os.path.getmtime(source)
        original_date = datetime.fromtimestamp(original_mtime)

        print(f"Original file modified: {original_date}")

        # Copy with metadata preservation
        copied_file = shutil.copy2(source, destination_dir)

        # Verify metadata was preserved
        copied_mtime = os.path.getmtime(copied_file)
        copied_date = datetime.fromtimestamp(copied_mtime)

        print(f"Copied file modified: {copied_date}")
        print(f"Metadata preserved: {original_date == copied_date}")

        return copied_file

    except Exception as e:
        print(f"Archive operation failed: {e}")
        return None

# Execute the archival
archived_file = archive_document(source_file, archive_directory)
if archived_file:
    print(f"Document successfully archived: {archived_file}")

I executed the above example code and added the screenshot below.

copy file python

The shutil.copy2() method is identical to shutil.copy() except it also preserves file metadata. This is crucial for document management systems where timestamps matter.

Method 4 – Use shutil.copyfile() for Content-Only Copying

When I need maximum performance and only want to copy file content (not permissions), I use Python’s shutil.copyfile() method.

import shutil
import time

# Example: Copying large data files for processing
source_data = "C:/DataSets/customer_transactions.csv"
processing_copy = "C:/Processing/temp_transactions.csv"

def copy_for_processing(source, destination):
    start_time = time.time()

    try:
        # Copy only file content (fastest method)
        shutil.copyfile(source, destination)

        end_time = time.time()
        copy_duration = end_time - start_time

        # Get file size for performance metrics
        file_size = os.path.getsize(destination)
        file_size_mb = file_size / (1024 * 1024)

        print(f"Copied {file_size_mb:.2f} MB in {copy_duration:.2f} seconds")
        print(f"Transfer rate: {file_size_mb/copy_duration:.2f} MB/s")

        return True

    except Exception as e:
        print(f"Copy operation failed: {e}")
        return False

# Execute the copy operation
success = copy_for_processing(source_data, processing_copy)
if success:
    print("Data file ready for processing")

Note that shutil.copyfile() requires the destination to be a file path, not a directory. It’s the fastest copying method, but it doesn’t preserve any file metadata or permissions.

Method 5 – Batch File Copying with Error Handling

In real-world scenarios, I often need to copy multiple files. Here’s a robust approach I’ve developed for batch operations.

import shutil
import os
import glob
from pathlib import Path

def batch_copy_files(source_pattern, destination_dir, file_extension="*"):
    """
    Copy multiple files matching a pattern to destination directory
    """
    # Ensure destination exists
    Path(destination_dir).mkdir(parents=True, exist_ok=True)

    # Find all matching files
    search_pattern = f"{source_pattern}/*.{file_extension}" if file_extension != "*" else f"{source_pattern}/*"
    source_files = glob.glob(search_pattern)

    if not source_files:
        print(f"No files found matching pattern: {search_pattern}")
        return []

    copied_files = []
    failed_files = []

    print(f"Found {len(source_files)} files to copy...")

    for i, source_file in enumerate(source_files, 1):
        try:
            filename = os.path.basename(source_file)
            destination = os.path.join(destination_dir, filename)

            # Skip if file already exists and is identical
            if os.path.exists(destination):
                if os.path.getsize(source_file) == os.path.getsize(destination):
                    print(f"[{i}/{len(source_files)}] Skipped (already exists): {filename}")
                    continue

            copied_file = shutil.copy2(source_file, destination)
            copied_files.append(copied_file)
            print(f"[{i}/{len(source_files)}] Copied: {filename}")

        except Exception as e:
            failed_files.append((source_file, str(e)))
            print(f"[{i}/{len(source_files)}] Failed: {filename} - {e}")

    # Summary
    print(f"\nBatch copy completed:")
    print(f"Successfully copied: {len(copied_files)} files")
    print(f"Failed: {len(failed_files)} files")

    if failed_files:
        print("\nFailed files:")
        for file_path, error in failed_files:
            print(f"  {os.path.basename(file_path)}: {error}")

    return copied_files

# Example usage: Copying all invoice PDFs to backup
source_directory = "C:/Invoices/2024"
backup_directory = "C:/Backup/Invoices/2024"

copied_invoices = batch_copy_files(source_directory, backup_directory, "pdf")

This batch copying approach includes progress tracking, duplicate detection, and comprehensive error reporting. It’s particularly useful for backup operations or data migration tasks.

Advanced File Copying Techniques

Let me explain to you some advanced file copying techniques.

Copy with Progress Monitoring

For large files, I often implement progress monitoring to keep users informed:

import shutil
import os

def copy_with_progress(src, dst):
    """
    Copy file with progress indication for large files
    """
    def progress_callback(copied_bytes, total_bytes):
        percentage = (copied_bytes / total_bytes) * 100
        print(f"\rProgress: {percentage:.1f}% ({copied_bytes}/{total_bytes} bytes)", end='')

    # Get file size
    file_size = os.path.getsize(src)

    if file_size > 50 * 1024 * 1024:  # Show progress for files > 50MB
        print(f"Copying large file ({file_size / 1024 / 1024:.1f} MB)...")

        # For large files, we'll copy in chunks and show progress
        with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
            copied = 0
            chunk_size = 1024 * 1024  # 1MB chunks

            while True:
                chunk = fsrc.read(chunk_size)
                if not chunk:
                    break

                fdst.write(chunk)
                copied += len(chunk)
                progress_callback(copied, file_size)

        print("\nCopy completed!")
    else:
        # Use standard copy for smaller files
        shutil.copy2(src, dst)

# Example usage
large_video = "C:/Videos/conference_recording.mp4"
backup_location = "C:/Backup/Videos/conference_recording.mp4"

copy_with_progress(large_video, backup_location)

Conditional Copying with Verification

Here’s a more sophisticated approach that includes file verification:

import shutil
import os
import hashlib

def copy_with_verification(src, dst, verify_hash=True):
    """
    Copy file and verify integrity using hash comparison
    """
    def calculate_file_hash(file_path):
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    
    try:
        # Calculate source file hash before copying
        if verify_hash:
            print("Calculating source file hash...")
            source_hash = calculate_file_hash(src)
        
        # Perform the copy operation
        print("Copying file...")
        copied_file = shutil.copy2(src, dst)
        
        # Verify the copied file
        if verify_hash:
            print("Verifying copied file...")
            destination_hash = calculate_file_hash(dst)
            
            if source_hash == destination_hash:
                print("✓ File integrity verified - copy successful!")
                return copied_file
            else:
                print("✗ File integrity check failed!")
                os.remove(dst)  # Remove corrupted copy
                return None
        
        return copied_file
        
    except Exception as e:
        print(f"Copy operation failed: {e}")
        return None

# Example: Copying critical financial data with verification
financial_data = "C:/FinancialData/quarterly_report.xlsx"
secure_backup = "C:/SecureBackup/quarterly_report_verified.xlsx"

verified_copy = copy_with_verification(financial_data, secure_backup)
if verified_copy:
    print(f"Secure backup created: {verified_copy}")

Real-World Example: Employee Document Management System

Here’s a complete example that demonstrates how I implemented a document management system using shutil.copy() methods in Python:

import shutil
import os
import json
from datetime import datetime
from pathlib import Path

class DocumentManager:
    def __init__(self, base_directory):
        self.base_dir = Path(base_directory)
        self.archive_dir = self.base_dir / "Archive"
        self.backup_dir = self.base_dir / "Backup"
        self.log_file = self.base_dir / "operations.log"
        
        # Create necessary directories
        self.archive_dir.mkdir(parents=True, exist_ok=True)
        self.backup_dir.mkdir(parents=True, exist_ok=True)
    
    def log_operation(self, operation, source, destination, status):
        """Log all file operations"""
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'operation': operation,
            'source': str(source),
            'destination': str(destination),
            'status': status
        }
        
        with open(self.log_file, 'a') as f:
            f.write(json.dumps(log_entry) + '\n')
    
    def archive_employee_document(self, employee_id, document_path):
        """Archive employee documents with proper organization"""
        try:
            source = Path(document_path)
            if not source.exists():
                raise FileNotFoundError(f"Document not found: {document_path}")
            
            # Create employee-specific archive folder
            employee_archive = self.archive_dir / str(employee_id)
            employee_archive.mkdir(exist_ok=True)
            
            # Generate archived filename with timestamp
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            archived_name = f"{timestamp}_{source.name}"
            destination = employee_archive / archived_name
            
            # Copy document to archive
            copied_file = shutil.copy2(source, destination)
            
            # Log successful operation
            self.log_operation("archive", source, destination, "success")
            print(f"Document archived: {copied_file}")
            
            return str(copied_file)
            
        except Exception as e:
            self.log_operation("archive", document_path, "N/A", f"failed: {e}")
            print(f"Archive failed: {e}")
            return None
    
    def backup_all_documents(self):
        """Create backup of all documents"""
        backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        current_backup = self.backup_dir / f"backup_{backup_timestamp}"
        current_backup.mkdir()
        
        total_files = 0
        copied_files = 0
        
        for root, dirs, files in os.walk(self.base_dir):
            # Skip backup and temporary directories
            if any(skip in root for skip in ['Backup', 'Temp', '__pycache__']):
                continue
                
            for file in files:
                if file.endswith(('.tmp', '.log')):
                    continue
                    
                total_files += 1
                source_file = Path(root) / file
                
                # Maintain directory structure in backup
                relative_path = source_file.relative_to(self.base_dir)
                backup_file = current_backup / relative_path
                backup_file.parent.mkdir(parents=True, exist_ok=True)
                
                try:
                    shutil.copy2(source_file, backup_file)
                    copied_files += 1
                except Exception as e:
                    print(f"Failed to backup {source_file}: {e}")
        
        print(f"Backup completed: {copied_files}/{total_files} files backed up to {current_backup}")
        return current_backup

# Example usage of the document management system
def demo_document_system():
    # Initialize document manager
    doc_manager = DocumentManager("C:/CompanyDocuments")
    
    # Simulate archiving employee documents
    employee_docs = [
        (12345, "C:/Temp/john_doe_contract.pdf"),
        (12346, "C:/Temp/jane_smith_evaluation.docx"),
        (12347, "C:/Temp/bob_wilson_timesheet.xlsx")
    ]
    
    print("=== Employee Document Archival ===")
    for emp_id, doc_path in employee_docs:
        # In real scenario, these files would exist
        print(f"Processing employee {emp_id} document...")
        archived = doc_manager.archive_employee_document(emp_id, doc_path)
        if archived:
            print(f"✓ Successfully archived to: {archived}")
    
    print("\n=== System Backup ===")
    backup_location = doc_manager.backup_all_documents()
    print(f"System backup created at: {backup_location}")

# Run the demonstration
demo_document_system()

This document management system efficiently organizes employee files by archiving them with timestamps and backing up the entire document repository while preserving folder structure. It also maintains detailed logs for tracking all file operations, ensuring reliability and easy auditing.

The key to successful file operations lies in proper error handling, understanding the differences between copy methods, and choosing the right approach for your specific use case.

Start with the basic shutil.copy() for simple operations, then graduate to shutil.copy2() when metadata preservation matters, and use shutil.copyfile() for performance-critical applications.

You may also read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.