概要(shutil.move は「移動・リネーム」を1行で安全にやる標準関数)
shutil.move は、ファイル/ディレクトリを別の場所へ移動したり、名前を変更(リネーム)するための標準ライブラリの関数です。移動先が同じファイルシステムなら高速にリネームし、別ファイルシステムならコピー+元の削除まで面倒を見てくれます。重要なのは「移動先が“ディレクトリかファイル名か”で挙動が変わる」「親ディレクトリは先に作る」「上書きや衝突を自分で制御する」の3点です。
基本の使い方(ここが重要)
ファイルを別フォルダへ移す(フォルダ内にそのまま入る)
import shutil
# src を dst_dir 配下へ移動(ファイル名はそのまま)
shutil.move("downloads/report.pdf", "documents")
# → documents/report.pdf
Pythondst が既存のディレクトリなら「その中へ入る」動きになります。存在しない親フォルダは自動作成されないため、事前に作っておきます。
移動と同時に名前も変える(リネーム)
import shutil
# ファイル名を変えながら移動
shutil.move("downloads/old.txt", "archive/new_name.txt")
Pythondst が“ディレクトリでないパス”なら、その名前で移動(リネーム)します。
ディレクトリを丸ごと移動
import shutil
# ディレクトリごと移動(ツリー丸ごと)
shutil.move("data/raw", "data/processed/raw")
Python丸ごと移動できますが、移動先に同名ディレクトリがあると衝突するため、事前に存在確認や削除/別名を検討します。
実務の安全設計(親作成・衝突回避・例外対応)
親ディレクトリは先に用意する(pathlib を併用)
from pathlib import Path
import shutil
dst = Path("archive/2025/report.csv")
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move("downloads/report.csv", dst) # 文字列でも Path でもOK
Pythonmove は“親を作らない”ので、必ず先に mkdir(parents=True) で用意してから呼びます。
上書き・衝突を自分で制御する(安全なラッパの例)
from pathlib import Path
import shutil
def safe_move(src: Path, dst: Path, overwrite=False):
src = Path(src); dst = Path(dst)
dst.parent.mkdir(parents=True, exist_ok=True)
if dst.exists():
if not overwrite:
raise FileExistsError(f"移動先が既に存在: {dst}")
# 上書き許可なら、既存を消す/別名付与など方針を決める
if dst.is_file():
dst.unlink()
else:
# ディレクトリ既存なら別名へ退避などを検討
raise IsADirectoryError(f"ディレクトリが既に存在: {dst}")
shutil.move(str(src), str(dst))
Python環境によっては既存ファイルがあると失敗/上書きになる挙動差があるため、明示的なポリシーで揃えます。
代表的な例外への備え(最低限のガード)
- FileNotFoundError: 移動元が存在しない(パス間違い)
- PermissionError: 権限不足(読み/書き/削除不可)
- shutil.Error: 自己移動や不正な操作などの論理的エラー
発生し得る例外を想定し、ユーザー向けの分かりやすいメッセージと代替動作(別名、スキップ、ログ記録)を用意します。
よく使うパターン(一括移動・リネーム規則・ログ整理)
パターン一致でまとめて移動(glob と組み合わせ)
from pathlib import Path
import shutil
src_dir = Path("downloads")
dst_dir = Path("archive/docs")
dst_dir.mkdir(parents=True, exist_ok=True)
for f in src_dir.glob("*.pdf"):
shutil.move(str(f), str(dst_dir / f.name))
Python拡張子や接頭辞で選んで、一括移動します。取得順はファイルシステム依存なので、必要なら sorted で並べます。
規則に沿って“名前替え+移動”
from pathlib import Path
import shutil
src = Path("images")
dst = Path("images/renamed")
dst.mkdir(parents=True, exist_ok=True)
for i, f in enumerate(sorted(src.glob("*.png"), key=lambda p: p.stat().st_mtime), start=1):
new = dst / f"{i:03d}_{f.stem}.png"
shutil.move(str(f), str(new))
Python「時刻順に連番+元名の一部」を付けるなど、規則を決めて整然と保存します。
“ログローテーション風”の安全移動
from pathlib import Path
import shutil
log = Path("logs/app.log")
rotated = log.with_name(f"app.log.1")
# 既存があればさらにずらすなどのポリシーも検討
if rotated.exists():
rotated.unlink()
shutil.move(log, rotated)
Python事前に既存ファイルの扱い(削除/連番ずらし)を決めてから move します。
深掘りポイント(os.rename との違い・同一/別FS・原子性)
os.rename との違い(基本はリネーム、move は“賢い”移動)
- os.rename は同一ファイルシステム内のリネームが得意。別FSへの移動は失敗することがあります。
- shutil.move は、同一FSならリネーム、別FSならコピー+元の削除を内部で切り替えます。これが「面倒を見てくれる」所以です。
同一/別ファイルシステムでの挙動
- 同一FS: 高速・ほぼ即時に完了(メタデータの更新中心)
- 別FS: 実際にデータコピーが走るため、サイズやディスク速度に比例して時間がかかる。失敗時のリカバリ(途中で止まる)の設計を考えると安心です。
“原子性”は用途による
move(特に別FS)では途中失敗により「元と先のどちらにもない」状態や「両方にある」状態が起き得ます。業務で重要なら、一時ファイル名へ移動→検証→本番名へ最終リネーム(同一FSで原子的)と段階化すると安全性が上がります。
例題で身につける(定番から一歩先まで)
例題1:親ディレクトリを用意してから安全移動
from pathlib import Path
import shutil
src = Path("downloads/report_2025.pdf")
dst = Path("documents/reports/report_2025.pdf")
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move(src, dst)
print("moved to:", dst)
Python例題2:衝突を避けて“タイムスタンプ付”で移動
from pathlib import Path
from datetime import datetime
import shutil
src = Path("logs/app.log")
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
dst = src.with_name(f"app_{ts}.log")
dst.parent.mkdir(parents=True, exist_ok=True)
if dst.exists():
raise FileExistsError("意図外の衝突")
shutil.move(src, dst)
Python例題3:拡張子ごとにサブフォルダへ分類移動
from pathlib import Path
import shutil
box = Path("inbox")
out = Path("sorted"); out.mkdir(parents=True, exist_ok=True)
groups = {".png": "images", ".csv": "tables", ".txt": "texts"}
for f in box.iterdir():
if f.is_file():
sub = groups.get(f.suffix.lower(), "others")
target = out / sub / f.name
target.parent.mkdir(parents=True, exist_ok=True)
shutil.move(f, target)
Python例題4:エラーを捕まえてメッセージを出す
import shutil
from pathlib import Path
try:
shutil.move("missing.txt", "archive/missing.txt")
except FileNotFoundError:
print("移動元がありません")
except PermissionError:
print("権限不足です")
Pythonまとめ
shutil.move は「移動」と「リネーム」を一発でこなす標準関数です。移動先がディレクトリかファイル名かで挙動が変わる点、親フォルダは先に作る点、上書き・衝突を自分で制御する点が最重要。別ファイルシステムではコピー+削除になるため時間がかかり、原子性は用途次第。pathlib と組み合わせて、結合→親作成→衝突ガード→移動の順に書けば、短くて安全なファイル整理・ログローテーション・一括分類が安定して動きます。
