High-Level File Operations with shutil
Copy files and directory trees, move and remove paths carefully, inspect disk space, and work with archives using Python’s shutil module.
Intermediate
Python’s shutil module provides high-level operations for copying files and directory trees, moving paths, removing directory trees, checking disk space, and creating or extracting archives. It complements pathlib, which represents and inspects paths, and File I/O, which reads and writes file contents.
Choose the right filesystem tool
For Python 3.14 and later, pathlib also has Path.copy(), Path.copy_into(), Path.move(), and Path.move_into() for path-oriented copy and move operations. This lesson uses shutil operations that can also be run on Python 3.13, so the examples are locally verifiable. shutil remains useful for configurable directory-tree copies, recursive removal, disk usage, and archive helpers. Use the API your supported Python version provides, and do not assume a copy preserves every kind of filesystem metadata.
Copy files and understand metadata
copyfile(source, destination) copies file contents to the exact destination filename. copy(source, destination) also copies permission mode and allows destination to be a directory. copy2() attempts to preserve additional metadata such as timestamps, but no copy function can preserve every platform-specific attribute. Existing destination files may be replaced, so choose the destination deliberately.
Output
Ready to run.
Copy directory trees with exclusions
copytree() copies a directory and its descendants. By default, the destination must not already exist. Pass ignore=ignore_patterns(...) to skip names that match shell-style filename patterns at each visited directory. If dirs_exist_ok=True is used, files at matching destination paths can be overwritten; set it only when that behavior is intended.
Output
Ready to run.
Move files or directories
shutil.move() moves a file or directory and returns the resulting destination path. A move on the same filesystem normally uses a rename operation. If that cannot work, such as when moving across filesystems, shutil may copy the data and then remove the source. Do not assume the operation is atomic across filesystems. Python 3.14+ also provides Path.move() for path-oriented moves.
Output
Ready to run.
Remove a directory tree deliberately
rmtree() removes a directory and its contents, so using the wrong path can destroy valuable data. Restrict removal to a path your program created or has explicitly validated. Symlink-attack resistance depends on platform capabilities; check rmtree.avoids_symlink_attacks when that property matters. Do not silence removal errors without a clear recovery policy.
Output
Ready to run.
Inspect available disk space
disk_usage(path) returns total, used, and free space in bytes for the filesystem containing a file or directory. These values depend on the mounted filesystem and can change while a program runs, so use them as a current measurement rather than a guarantee that a later write will succeed.
Output
Ready to run.
Create and extract archives safely
make_archive() creates a supported archive from a directory tree; unpack_archive() extracts a recognized format. For tar-based archives, use the data extraction filter when appropriate. That filter reduces dangerous path and link behavior, but it does not make every untrusted archive harmless: inspect its contents and choose a controlled destination. The filter parameter is not accepted for ZIP archives. More specialized zipfile and tarfile operations belong to their own APIs.
Output
Ready to run.
The examples use temporary workspaces so copying, moving, deleting, and archive extraction never touch a learner’s real project files. Start by inspecting the source and destination paths, then use the narrowest operation that fits the task.
Quick check
Which operation copies file contents to an exact destination filename without attempting to copy permission bits or other metadata?
from pathlib import Path
from tempfile import TemporaryDirectory
import shutil
with TemporaryDirectory() as folder:
root = Path(folder)
source = root / "source.txt"
destination = root / "copy.txt"
source.write_text("hello", encoding="utf-8")
assert destination.read_text(encoding="utf-8") == "hello"
print(destination.exists())
Quick check
Which call copies a directory tree while skipping entries whose names match `*.tmp`?
from pathlib import Path
import shutil
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
root = Path(folder)
source = root / "source"
source.mkdir()
(source / "keep.txt").write_text("keep", encoding="utf-8")
(source / "skip.tmp").write_text("skip", encoding="utf-8")
destination = root / "backup"
assert (destination / "keep.txt").exists()
assert not (destination / "skip.tmp").exists()
print("copy filtered")
Quick check
A temporary build directory contains nested files. Which function removes the directory tree rather than requiring the directory to be empty?
from pathlib import Path
import shutil
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
workspace = Path(folder) / "build"
(workspace / "cache").mkdir(parents=True)
(workspace / "cache" / "temp.txt").write_text("remove", encoding="utf-8")
shutil.(workspace)
assert not workspace.exists()
print("removed")
Quick check
Which attribute reports currently available disk space in bytes?
import shutil
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
usage = shutil.disk_usage(folder)
print(usage.)
assert usage.free >= 0
print(usage.total > 0)
Quick check
When extracting a tar-based archive with unpack_archive(), which filter is the documented safer choice for ordinary data files?
from pathlib import Path
import shutil
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
root = Path(folder)
project = root / "project"
project.mkdir()
(project / "note.txt").write_text("safe data", encoding="utf-8")
archive = shutil.make_archive(str(root / "snapshot"), "gztar", root_dir=str(project))
destination = root / "restored"
destination.mkdir()
shutil.unpack_archive(archive, destination, )
assert (destination / "note.txt").read_text(encoding="utf-8") == "safe data"
print("restored")
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.