Learn by reading through in order

shutil and tempfile — Bulk File Ops and Temp Files

Learn Python's shutil and tempfile from the ground up. Bulk file operations with shutil.copy / shutil.move / shutil.rmtree, plus how to safely use temporary files via tempfile.NamedTemporaryFile — all hands-on.

This article walks through two modules: shutil, which operates on whole files and folders, and tempfile, which handles disposable temporary files. You'll cover bulk file operations with shutil.copy / move / rmtree, the safe way to use NamedTemporaryFile, and the atomic write pattern that combines both.

shutil — File and Folder Operations in One Line

shutil stands for shell utilities — it's the standard library that lets you call shell-style operations like cp / mv / rm -r from Python in a single line. The os module has things like os.rename, but shutil adds higher-level operations such as recursive folder copy and tree delete.

Three Core shutil Functions
shutil.copy(src, dst)Make a copyshutil.move(src, dst)Move or renameshutil.rmtree(path)Recursive delete
copy duplicates a file to another location, move moves it (or renames it in place), and rmtree recursively deletes an entire folder. These three cover most day-to-day operations.
FunctionBehaviorNotes
shutil.copy(src, dst)Duplicate a fileIf dst is a folder, it goes inside with the same name
shutil.copy2(src, dst)copy plus metadata like modification timeGood for backups
shutil.copytree(src, dst)Recursively duplicate an entire folderdst **must not exist**
shutil.move(src, dst)Move or rename a file or folderEquivalent to rename when src and dst share the same parent
shutil.rmtree(path)Recursively delete a folder and its contentsNot reversible — double-check the target before running
import shutil
import os

# 1) Copy a file to another location (a typical backup)
os.makedirs("backups", exist_ok=True)        # Make sure the destination folder exists
shutil.copy("data/sales.csv", "backups/sales_2024.csv")

shutil.move("logs/temp.log", "logs/archive_2024.log")

shutil.rmtree("old_data")

rmtree Cannot Be Undone

shutil.rmtree(path) physically deletes everything under path. Python doesn't route through a trash bin — it's gone instantly. In a real project, print the target before deleting, add a guard like if path.startswith("/tmp"), and build habits that protect both you and your code.

Back up the sales CSV data/sales.csv as backups/sales_backup.csv. You can inspect the original from the 📂 Files panel on the left.

① Please import shutil and os.

② Please prepare the destination folder backups/ (in a way that doesn't error if it already exists).

③ Please copy the file to the destination name above.

④ Please verify both the backup and the original by printing Backup done: True / Source still exists: True.

(If you run it correctly the explanation appears below.)

Python Editor

Run code to see output

tempfile — Safely Create Disposable Files

tempfile is the standard library for temporary files that are automatically cleaned up when you're done with them. You use it for storing intermediate results, buffering large data, holding test working folders — anything that only needs to exist while the process is running.

If you craft your own filenames like tmp_xxx.txt, you risk colliding with other processes or forgetting to delete them. tempfile instead creates files in the OS's temp folder under a guaranteed-unique name, and removes them automatically when the work is done.

NamedTemporaryFile Lifecycle
with NamedTemporaryFile():→ Temp file createdInside the block:write and readLeave the block→ Auto-deleteUse it safelywithout cleanup
The file is created the moment you enter the with block, and is deleted automatically when you leave it. The filename is assigned on the spot to avoid collisions — you don't pick it yourself.
Function / attributeMeaningNotes
tempfile.NamedTemporaryFileTemp file you open and close with `with`delete=False keeps it after the block
tempfile.mkdtemp()Returns the path of a temp folderYou delete it yourself with shutil.rmtree
tempfile.gettempdir()Returns the OS temp folder path/tmp on Linux, different path on macOS
tf.nameThe actual file path (random name)Accessible inside the `with` block
tf.write / tf.readSame API as a regular file objectPick 'w' / 'r' / 'w+' via the mode argument
import tempfile
import os

# Open safely with `with`, then auto-delete
with tempfile.NamedTemporaryFile(mode="w+", suffix=".txt", delete=True) as tf:
    tf.write("intermediate aggregation data\n")
    tf.seek(0)
    print("Content:", tf.read())
    print("Path:", tf.name)
# ← The block ends here and the file is auto-deleted

When to Combine delete=False with `with`

When you want to write the file and read it back later from somewhere else, pass delete=False so the file survives leaving the with block. Once you're done with it, either remove it yourself with os.remove(path) or swap it into the real destination with shutil.move — that's the atomic write pattern covered below.

Create a temporary file, write to it, then check whether it exists before and after deletion. Open a NamedTemporaryFile with delete=False, write to it, read it back, check existence before deletion, delete it, then check existence again.

① Please import tempfile and os.

② Please open a temp file with NamedTemporaryFile(mode="w", delete=False), write the string sales total: 3750 into it, save tf.name to a path variable, and exit the with block.

③ Please print existence with os.path.exists(path) in the form Exists before delete: ◯◯.

④ Please delete the file with os.unlink(path).

⑤ Please print existence again with os.path.exists(path) in the form Exists after delete: ◯◯.

Python Editor

Run code to see output

Auto-Deleted Temp Files with `delete=True`

If you overwrite an important config file or state file directly with open("settings.json", "w"), a process crash mid-write can leave a half-written file behind, which then fails to parse on the next read. The standard way to avoid this is the "write to a temp file completely, then rename / move it into the real path" pattern — known as atomic write (atomic — even if interrupted, observers can only see "completed" or "not started", never an in-between state).

Flow of an Atomic Write
Direct open("w")Crash mid-writeBroken fileleft behindWrite fullyto tempfileshutil.move tothe real pathSwap isinstant from outsideBroken statenever visible
Writing directly leaves a broken file behind on a crash, but going through a temp file swaps to the real path only at the moment writing is complete — so the broken state is never visible.
import tempfile
import shutil
import json

final_path = "settings.json"
new_settings = {"theme": "dark", "lang": "en", "fontSize": 14}

# 1) Write fully to a temp file
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as tf:
    json.dump(new_settings, tf)
    temp_path = tf.name

# 2) Once finished, move into the real path (this single moment is the "swap")
shutil.move(temp_path, final_path)

# 3) Verify
with open(final_path) as f:
    print(json.load(f))
# → {'theme': 'dark', 'lang': 'en', 'fontSize': 14}

A file object has a current read/write position (cursor), and calling `tf.write(...)` moves the cursor right after what was written (= the end). Calling tf.read() from there returns an empty string because there's nothing past the cursor, so the standard move is to reset the cursor to the start with `tf.seek(0)` before reading.

How the File Cursor and seek(0) Behave
write("config: ok")Cursor → endread()→ "" (empty)seek(0)Cursor → startread()→ "config: ok"
write moves the cursor to the end → reading from there returns an empty stringseek(0) brings the cursor back to the start → read returns what you wrote.

mode="w+" Is Read-Write Mode

`mode="w+"` is the mode that lets you both write and read through the same file handle. With just "w" you're write-only and read() doesn't work; with just "r" you're read-only and write() doesn't work. For exercises that write and immediately read back, "w+" is required.

Create a temp file with `with NamedTemporaryFile(delete=True)` and confirm it's auto-deleted. Write to it inside the with block, read it back, then check existence after the block exits — it should be False.

① Please import tempfile and os.

② Please open tempfile.NamedTemporaryFile(mode="w+", delete=True) with a with statement (mode="w+" is read-write).

③ Inside the block, please write tf.write("config: ok\n"), reset the cursor to the start with tf.seek(0), read back with tf.read(), and print it as Content: ◯◯.

④ Please save the path inside the block as saved_path = tf.name.

⑤ After leaving the block, please print the result of os.path.exists(saved_path) in the form Exists after with: ◯◯ (it should be False).

Python Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1Which of the following do you use when you want to recursively delete an entire folder?

Q2Which is a typical reason to use tempfile.NamedTemporaryFile with delete=False?

Q3What's the essence of atomic write?