順番に読み進めながら学べます

shutil と tempfile — ファイル一括操作と一時ファイル

Python の shutil と tempfile を基礎から解説します。shutil.copy / shutil.move / shutil.rmtree によるファイル一括操作と、tempfile.NamedTemporaryFile による一時ファイルの安全な使い方を、ハンズオンで学べます。

ファイルとフォルダを丸ごと操作する shutil と、使い捨ての一時ファイル を扱う tempfile の 2 つを整理します。shutil.copy / move / rmtree によるファイル一括操作、NamedTemporaryFile の安全な使い方、両者を組み合わせたアトミック書き込みパターンまで扱います。

shutil — ファイルとフォルダの操作を 1 行で書く

shutilshell utilities の略で、シェル(コマンドライン)で cp / mv / rm -r のように使う操作を Python のコードから 1 行で呼び出せる 標準ライブラリです。os モジュールにも os.rename などはありますが、shutil再帰的なフォルダコピーツリー削除 といった、よりまとまった単位の操作を提供します。

shutil の主要関数 3 種類
shutil.copy(src, dst)複製を作るshutil.move(src, dst)移動 or 改名shutil.rmtree(path)再帰的に削除
copy はファイルを別の場所に複製、move は移動(または同じ場所での名前変更)、rmtree はフォルダごと再帰的に削除。3 つを覚えるだけで日常の運用処理はほぼカバーできる。
関数動作備考
shutil.copy(src, dst)ファイルを複製するdst がフォルダなら同名でその中に作る
shutil.copy2(src, dst)copy + 更新日時などのメタ情報も保持バックアップ用途に向く
shutil.copytree(src, dst)フォルダごと再帰的に複製するdst は **存在してはいけない**
shutil.move(src, dst)ファイルやフォルダを移動・名前変更する同じパス上なら rename と等価
shutil.rmtree(path)フォルダと配下を再帰的に削除する戻せない。実行前に対象パスをよく確認する
import shutil
import os

# 1) ファイルを別の場所にコピー (バックアップを作る典型例)
os.makedirs("backups", exist_ok=True)        # コピー先のフォルダを用意
shutil.copy("data/sales.csv", "backups/sales_2024.csv")

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

shutil.rmtree("old_data")

rmtree は取り消せない

shutil.rmtree(path)path 配下を 物理削除 します。Python では「ゴミ箱」を経由せず、即座に消えます。実プロジェクトで使うときは消す対象を print で確認する、if path.startswith("/tmp") のようなガードを入れる など、人間と機械の両方で守る慣習を付けましょう。

売上 CSV data/sales.csvbackups/sales_backup.csv としてバックアップします。左の 📂 ファイルから元の中身が確認できます。

① shutil と os を読み込んでください

② コピー先フォルダ backups/ を準備してください(既に存在してもエラーにしない方法で)

③ ファイルを指定の名前でコピーしてください

④ バックアップ先と元ファイル両方の存在を バックアップ完了: True / 元ファイルもある: True の形で表示して確認してください

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

tempfile — 安全に使い捨てファイルを作る

tempfile使い終わったら自動で消える一時ファイル を作るための標準ライブラリです。中間結果の保存・大量データの一時バッファ・テストの作業フォルダなど、「処理中だけ存在すれば良いファイル」のために使います。

手で tmp_xxx.txt のようなファイル名を考えると 他のプロセスとぶつかったり消し忘れたり しがちですが、tempfileOS の一時フォルダ重複しないファイル名 で作り、処理が終わったら自動で消してくれます。

NamedTemporaryFile のライフサイクル
with NamedTemporaryFile():→ 一時ファイル作成ブロック内で書き込み・読み出しブロックを抜ける→ 自動削除後始末を意識せず安全に使える
with ブロックに入った瞬間にファイルが作られ、ブロックを抜けるときに 自動で削除 される。ファイル名はその場で衝突しない名前が割り当てられるので、自分で命名する必要がない。
関数・属性意味備考
tempfile.NamedTemporaryFilewith で開閉する一時ファイルdelete=False で抜けても残す
tempfile.mkdtemp()一時フォルダのパスを返す削除は自分で shutil.rmtree
tempfile.gettempdir()OS の一時フォルダのパスを返すLinux なら /tmp、macOS は別パス
tf.name実ファイルパス(ランダム名)with の中で参照できる
tf.write / tf.readファイルオブジェクトと同じ APImode 引数で 'w' / 'r' / 'w+' を選ぶ
import tempfile
import os

# with で安全に作って自動削除
with tempfile.NamedTemporaryFile(mode="w+", suffix=".txt", delete=True) as tf:
    tf.write("集計結果の中間データ\n")
    tf.seek(0)
    print("中身:", tf.read())
    print("パス:", tf.name)
# ← ここでブロックを抜けて自動削除される

delete=False と with を組み合わせる場面

書いてから別の場所で読み戻したい ときは delete=False を指定して、with を抜けてもファイルが残るようにします。残したファイルは 使い終わったら自分で os.remove(path) する か、後述のアトミック書き込みのように shutil.move で本来の保存先に置き換えます。

一時ファイルを作って書き込み、削除前と削除後の存在をそれぞれチェック します。delete=FalseNamedTemporaryFile で一時ファイルを作り、書き込み → 読み戻し → 削除前の存在確認 → 削除 → 削除後の存在確認、という流れを試します。

① tempfile と os を読み込んでください

NamedTemporaryFile(mode="w", delete=False) で一時ファイルを開き、sales total: 3750 という文字列を書き込んで、tf.name をパス変数に控えてから with ブロックを抜けてください

os.path.exists(パス)削除前の存在: ◯◯ の形で存在を表示してください

os.unlink(パス) でファイルを削除してください

⑤ もう一度 os.path.exists(パス)削除後の存在: ◯◯ の形で存在を表示してください

Python エディタ

コードを実行してください

`delete=True` で自動削除される一時ファイル

重要な設定ファイルや状態ファイルを直接 open("settings.json", "w") で書き換えると、書き込み途中でプロセスが落ちたとき にファイルが半分書かれた状態で残ってしまい、次回読み込み時にパースエラーになります。これを避ける定石が 「一時ファイルに書ききってから、本来の場所に rename / move する」 という アトミック書き込み(atomic、途中で中断されても「完了したか / していないか」しか観測できない更新方式)パターンです。

アトミック書き込みの流れ
直接 open("w")途中で落ちる壊れたファイルが残るtempfile に書き切るshutil.move で本物のパスへ外から見ると一瞬で切り替わる壊れた状態は見えない
直接書き だと途中で落ちると壊れたファイルが残るが、一時ファイル経由 だと書き終わった瞬間にだけ本物のパスに切り替わるので、「壊れた状態」が一切見えない
import tempfile
import shutil
import json

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

# 1) 一時ファイルに書き切る
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as tf:
    json.dump(new_settings, tf)
    temp_path = tf.name

# 2) 完了したら本物のパスに移動 (この瞬間だけが「切り替わり」のタイミング)
shutil.move(temp_path, final_path)

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

ファイルオブジェクトには 「現在の読み書き位置(カーソル)」 があり、`tf.write(...)` を呼ぶとカーソルは書いた直後の位置(= 末尾)に移動します。そのまま tf.read() を呼ぶと カーソルから後ろに何も無い ため空文字が返るので、`tf.seek(0)` でカーソルを先頭に戻してから 読み直す、というのが定石です。

ファイルカーソルと seek(0) の動き
write("config: ok")カーソルは末尾へread()→ ""(空文字)seek(0)カーソルを先頭へread()→ "config: ok"
write でカーソルが末尾へ進む → そのまま read すると 空文字 が返る → seek(0) で先頭に戻す → read で書いた内容が取れる。

mode="w+" は読み書き両用のモード

`mode="w+"`書き込みと読み込みを同じファイルハンドルで両方できる モードです。"w" だけだと書き込み専用で read() が使えず、"r" だけだと読み込み専用で write() が使えません。書いてすぐ読み戻す 演習では "w+" が必須になります。

`with NamedTemporaryFile(delete=True)` で一時ファイルを作って自動削除を確認 します。with ブロックの中で書き込んで読み戻し、ブロックを抜けた後に存在チェックをすると False になることを確認します。

① tempfile と os を読み込んでください

tempfile.NamedTemporaryFile(mode="w+", delete=True)with 文で開いてください(mode="w+" は読み書き両用)

③ ブロック内で tf.write("config: ok\n") のように書き込み、tf.seek(0) でカーソルを先頭に戻してから tf.read() で読み戻し、中身: ◯◯ の形で表示してください

④ ブロック内でパスを saved_path = tf.name として控えてください

⑤ ブロックを抜けた後、os.path.exists(saved_path) の結果を with 後の存在: ◯◯ の形で表示してください(False になるはずです)

Python エディタ

コードを実行してください
QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1次のうち、フォルダごと再帰的に削除 したいときに使うのはどれですか?

Q2tempfile.NamedTemporaryFiledelete=False 付き で使う典型的な理由として正しいのはどれですか?

Q3アトミック書き込み の本質は次のうちどれですか?