Học bằng cách đọc theo thứ tự

shutil và tempfile — Thao tác file hàng loạt và file tạm

Học shutil và tempfile của Python từ căn bản. Thao tác file hàng loạt với shutil.copy / shutil.move / shutil.rmtree, cùng cách dùng file tạm an toàn qua tempfile.NamedTemporaryFile — đều có thực hành.

Bài này đi qua hai module: shutil, thao tác trên cả file và folder, và tempfile, xử lý file tạm dùng một lần. Bạn sẽ phủ thao tác file hàng loạt với shutil.copy / move / rmtree, cách an toàn dùng NamedTemporaryFile, và pattern atomic write kết hợp cả hai.

shutil — Thao tác file và folder trong một dòng

shutil viết tắt của shell utilities — đây là thư viện chuẩn cho phép bạn gọi các thao tác kiểu shell như cp / mv / rm -r từ Python trong một dòng. Module os có những thứ như os.rename, nhưng shutil thêm các thao tác cấp cao hơn như copy folder đệ quyxóa cây.

Ba hàm cốt lõi của shutil
shutil.copy(src, dst)Tạo bản saoshutil.move(src, dst)Di chuyển hoặc đổi tênshutil.rmtree(path)Xóa đệ quy
copy nhân bản file đến vị trí khác, move di chuyển nó (hoặc đổi tên tại chỗ), và rmtree xóa đệ quy cả folder. Ba cái này phủ hầu hết thao tác hàng ngày.
HàmHành viGhi chú
shutil.copy(src, dst)Nhân bản fileNếu dst là folder, nó vào trong với cùng tên
shutil.copy2(src, dst)copy cộng metadata như thời gian sửa đổiTốt cho backup
shutil.copytree(src, dst)Nhân bản đệ quy cả folderdst **không được tồn tại**
shutil.move(src, dst)Di chuyển hoặc đổi tên file hoặc folderTương đương rename khi src và dst chia sẻ cùng cha
shutil.rmtree(path)Xóa đệ quy folder và nội dungKhông thể đảo ngược — kiểm tra kỹ mục tiêu trước khi chạy
import shutil
import os

# 1) Copy file đến vị trí khác (backup điển hình)
os.makedirs("backups", exist_ok=True)        # Đảm bảo folder đích tồn tại
shutil.copy("data/sales.csv", "backups/sales_2024.csv")

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

shutil.rmtree("old_data")

rmtree không thể hoàn tác

shutil.rmtree(path) xóa vật lý mọi thứ dưới path. Python không đi qua thùng rác — nó biến mất ngay lập tức. Trong dự án thật, hãy in mục tiêu trước khi xóa, thêm guard như if path.startswith("/tmp"), và xây thói quen bảo vệ cả bạn và code.

Backup CSV bán hàng data/sales.csv thành backups/sales_backup.csv. Bạn có thể kiểm tra file gốc từ panel 📂 Files bên trái.

① Hãy import shutil và os.

② Hãy chuẩn bị folder đích backups/ (theo cách không lỗi nếu nó đã tồn tại).

③ Hãy copy file đến tên đích ở trên.

④ Hãy xác minh cả backup và bản gốc bằng cách in Backup done: True / Source still exists: True.

(Nếu chạy đúng phần giải thích sẽ xuất hiện bên dưới.)

Python Editor

Chạy code để xem đầu ra

tempfile — Tạo file dùng một lần an toàn

tempfile là thư viện chuẩn cho file tạm tự động dọn dẹp khi bạn xong với chúng. Bạn dùng nó để lưu kết quả trung gian, đệm dữ liệu lớn, giữ folder làm việc test — bất cứ thứ gì chỉ cần tồn tại trong khi tiến trình chạy.

Nếu bạn tự làm tên file như tmp_xxx.txt, bạn có nguy cơ đụng với tiến trình khác hoặc quên xóa chúng. tempfile thay vào đó tạo file trong folder tạm của OS dưới tên đảm bảo duy nhất, và xóa chúng tự động khi việc xong.

Vòng đời NamedTemporaryFile
with NamedTemporaryFile():→ File tạm được tạoTrong block:ghi và đọcRời block→ Tự xóaDùng nó an toànkhông cần dọn
File được tạo lúc bạn vào block with, và tự động bị xóa khi bạn rời nó. Tên file được gán tại chỗ để tránh đụng — bạn không tự chọn nó.
Hàm / thuộc tínhÝ nghĩaGhi chú
tempfile.NamedTemporaryFileFile tạm bạn mở và đóng với `with`delete=False giữ nó sau block
tempfile.mkdtemp()Trả về đường dẫn folder tạmBạn tự xóa nó với shutil.rmtree
tempfile.gettempdir()Trả về đường dẫn folder tạm OS/tmp trên Linux, đường dẫn khác trên macOS
tf.nameĐường dẫn file thực (tên ngẫu nhiên)Truy cập được trong block `with`
tf.write / tf.readCùng API như đối tượng file thườngChọn 'w' / 'r' / 'w+' qua tham số mode
import tempfile
import os

# Mở an toàn với `with`, rồi tự xóa
with tempfile.NamedTemporaryFile(mode="w+", suffix=".txt", delete=True) as tf:
    tf.write("dữ liệu tổng hợp trung gian\n")
    tf.seek(0)
    print("Content:", tf.read())
    print("Path:", tf.name)
# ← Block kết thúc đây và file tự bị xóa

Khi nào kết hợp delete=False với `with`

Khi bạn muốn viết file và đọc lại nó sau từ chỗ khác, truyền delete=False để file sống sót khi rời block with. Một khi xong với nó, hoặc tự xóa với os.remove(path) hoặc swap nó vào đích thực với shutil.move — đó là pattern atomic write phủ bên dưới.

Tạo file tạm, ghi vào nó, rồi kiểm tra xem nó có tồn tại trước và sau khi xóa. Mở NamedTemporaryFile với delete=False, ghi vào nó, đọc lại, kiểm tra tồn tại trước khi xóa, xóa nó, rồi kiểm tra tồn tại lại.

① Hãy import tempfile và os.

② Hãy mở file tạm với NamedTemporaryFile(mode="w", delete=False), ghi chuỗi sales total: 3750 vào nó, lưu tf.name vào biến đường dẫn, và rời block with.

③ Hãy in tồn tại với os.path.exists(path) ở dạng Exists before delete: ◯◯.

④ Hãy xóa file với os.unlink(path).

⑤ Hãy in tồn tại lại với os.path.exists(path) ở dạng Exists after delete: ◯◯.

Python Editor

Chạy code để xem đầu ra

File tạm tự xóa với `delete=True`

Nếu bạn ghi đè file config quan trọng hoặc file state trực tiếp với open("settings.json", "w"), crash tiến trình giữa chừng ghi có thể để lại file viết một nửa, sau đó fail parse ở lần đọc tiếp. Cách chuẩn để tránh điều này là pattern "ghi đầy đủ vào file tạm, rồi rename / move nó vào đường dẫn thực" — gọi là atomic write (atomic — kể cả bị gián đoạn, người quan sát chỉ có thể thấy "hoàn thành" hoặc "chưa bắt đầu", không bao giờ trạng thái giữa chừng).

Luồng của một Atomic Write
Mở trực tiếp("w")Crash giữa chừngFile hỏngbị bỏ lạiGhi đầy đủvào tempfileshutil.move sangđường dẫn thựcSwap tức thìtừ bên ngoàiTrạng thái hỏngkhông bao giờ thấy
Ghi trực tiếp để lại file hỏng khi crash, nhưng đi qua file tạm swap đến đường dẫn thực chỉ tại thời điểm ghi xong — nên trạng thái hỏng không bao giờ thấy được.
import tempfile
import shutil
import json

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

# 1) Ghi đầy đủ vào file tạm
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as tf:
    json.dump(new_settings, tf)
    temp_path = tf.name

# 2) Khi xong, move vào đường dẫn thực (khoảnh khắc đơn này là "swap")
shutil.move(temp_path, final_path)

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

Đối tượng file có vị trí đọc/ghi hiện tại (cursor), và gọi `tf.write(...)` di chuyển cursor ngay sau cái đã được ghi (= cuối). Gọi tf.read() từ đó trả về chuỗi rỗng vì không có gì sau cursor, nên nước đi chuẩn là reset cursor về đầu với `tf.seek(0)` trước khi đọc.

Cursor file và seek(0) hoạt động ra sao
write("config: ok")Cursor → cuốiread()→ "" (rỗng)seek(0)Cursor → đầuread()→ "config: ok"
write di chuyển cursor đến cuối → đọc từ đó trả về chuỗi rỗngseek(0) đưa cursor về đầu → đọc trả về cái bạn đã viết.

mode="w+" là Read-Write Mode

`mode="w+"` là mode cho phép bạn vừa ghi vừa đọc qua cùng file handle. Với chỉ "w" bạn chỉ ghi và read() không hoạt động; với chỉ "r" bạn chỉ đọc và write() không hoạt động. Cho bài tập viết và đọc lại ngay, "w+" là bắt buộc.

Tạo file tạm với `with NamedTemporaryFile(delete=True)` và xác nhận nó tự xóa. Ghi vào nó trong block with, đọc lại, rồi kiểm tra tồn tại sau khi block thoát — nó nên là False.

① Hãy import tempfile và os.

② Hãy mở tempfile.NamedTemporaryFile(mode="w+", delete=True) với câu lệnh with (mode="w+" là read-write).

③ Trong block, hãy ghi tf.write("config: ok\n"), reset cursor về đầu với tf.seek(0), đọc lại với tf.read(), và in nó dạng Content: ◯◯.

④ Hãy lưu đường dẫn trong block dưới dạng saved_path = tf.name.

⑤ Sau khi rời block, hãy in kết quả của os.path.exists(saved_path) ở dạng Exists after with: ◯◯ (nó nên là False).

Python Editor

Chạy code để xem đầu ra
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Cái nào sau đây bạn dùng khi muốn xóa đệ quy cả folder?

Câu 2Lý do điển hình để dùng tempfile.NamedTemporaryFile với delete=False là gì?

Câu 3Bản chất của atomic write là gì?