Copy files and directory trees in Python

Use shutil.copy, copy2, and copytree, understand metadata limits and overwrites, and handle errors without treating a file copy as a complete backup.

Best practices
Python programming and application logging

Use shutil.copy() for file content and permission bits, shutil.copy2() when you also want supported metadata copied, and shutil.copytree() for a directory tree.

Copy one file

from pathlib import Path
import shutil

source = Path("input/report.txt")
destination = Path("output/report.txt")
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(source, destination)

The source must exist. If the destination is an existing directory, copy() uses the source's filename inside it. If it names an existing file, that file is replaced. Make the overwrite behavior deliberate.

Preserve supported metadata

shutil.copy2(source, destination)

copy2() attempts to preserve metadata such as permission bits and timestamps. It cannot preserve everything on every platform: ownership, access-control lists, and platform-specific data can require separate handling. It is useful for file-copy workflows but does not by itself provide a complete backup or a consistent snapshot of a changing file.

Copy a directory tree

shutil.copytree("input/project", "output/project")

The destination must normally be absent. On Python 3.8 and later, dirs_exist_ok=True permits existing destination directories:

shutil.copytree("input/project", "output/project", dirs_exist_ok=True)

This can overwrite destination files with matching names; it does not remove unrelated files already there. By default, symbolic links are followed and their targets copied. Review symlinks=True if preserving links is required.

Function Content Additional behavior
copyfile() One file Does not copy permission bits or timestamps
copy() One file Copies permission bits
copy2() One file Attempts to copy supported metadata
copytree() A directory tree Uses copy2() for files by default and copies directory metadata

FAQ

Will a large file be loaded entirely into memory? These functions do not ordinarily read the entire file into a Python buffer. Depending on the platform, Python can use operating-system fast-copy operations or buffered copying. Avoid assuming one fixed buffer size or a universal memory guarantee.

How should I build portable paths? Pass pathlib.Path objects, as above, or construct paths with os.path.join().

Which errors should I handle? Common cases include a missing source (FileNotFoundError), insufficient access (PermissionError), and copying a file onto itself (shutil.SameFileError). A failed copy may leave partial output; successful return is not proof of an atomic replacement or durable storage.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting