はじめに
Python でファイルの コピー・移動・削除 を行うとき、
- パスの扱い →
pathlib.Path - 実際のコピー・移動系 →
shutilやPathメソッド
という組み合わせが 一番気持ちいい構成 です。
1. 基本スタイル:Path を使ってから shutil に渡す
まずはお作法から。
from pathlib import Path
import shutil
src = Path("data/input.txt")
dst = Path("backup/input.txt")
dst.parent.mkdir(parents=True, exist_ok=True) # 必要ならフォルダ作成
shutil.copy2(src, dst)
ポイント:
- パスは 全部 Path オブジェクト にする
-
shutilにはstr(src)ではなく そのまま Path を渡してOK(対応済み) -
dst.parent.mkdir()で、コピー先フォルダも楽に作れる
2. ファイルコピー:shutil.copy / copy2 / copytree
2.1 単純コピー:shutil.copy
from pathlib import Path
import shutil
src = Path("data/report.txt")
dst = Path("backup/report.txt")
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src, dst)
- 中身+パーミッション(モード)をコピー
- タイムスタンプなどのメタ情報は簡略的
2.2 メタ情報も含めてコピー:copy2
shutil.copy2(src, dst)
- 更新日時などのメタデータも可能な限りコピーしたいなら
copy2推奨 - バックアップ用途やログ保全などに使いやすい
2.3 ディレクトリごとコピー:copytree
src_dir = Path("data")
dst_dir = Path("backup/data_copy")
shutil.copytree(src_dir, dst_dir)
注意点:
-
dst_dirが 既に存在しているとエラー - Python 3.8+ なら
dirs_exist_ok=Trueが使える:
shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)
3. ファイル移動:shutil.move or Path.rename
3.1 一番素直なのは shutil.move
from pathlib import Path
import shutil
src = Path("data/report.txt")
dst = Path("archive/2025/report.txt")
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move(src, dst)
特徴:
- ファイルでもディレクトリでも OK
- 同一ドライブ間なら実質
rename、ドライブ跨ぎならコピー+削除で対応 - 「とにかく移動したい」時はこれを使うのが無難
3.2 シンプルリネームなら Path.rename
p = Path("data/report.txt")
p.rename("data/report_old.txt")
もしくは Path 同士:
p = Path("data/report.txt")
new_path = p.with_name("report_old.txt")
p.rename(new_path)
注意:
- 同じファイルシステム内での移動・名前変更 に向いている
- 別ドライブへの移動などには対応していない(OS依存で失敗することも)
4. 削除:Path.unlink / shutil.rmtree
4.1 ファイル削除:unlink
from pathlib import Path
p = Path("data/tmp.txt")
if p.exists():
p.unlink()
-
unlink()は「ファイル削除」 -
is_file()でチェックしてから消すのもアリ:
if p.is_file():
p.unlink()
4.2 空のディレクトリ削除:rmdir
d = Path("data/tmp_folder")
d.rmdir()
- 中身があるとエラー
- 「空フォルダだけ整理したい」とき用
4.3 中身ごとディレクトリ削除:shutil.rmtree
import shutil
from pathlib import Path
d = Path("data/tmp_folder")
if d.exists():
shutil.rmtree(d)
- サブディレクトリ・ファイル含め 全部まとめて削除
- 強力なのでパス確認は慎重に(
print(d.resolve())で事前確認おすすめ)
5. ちょっと実践:バックアップ+古いバックアップ削除
要件
-
data/以下の内容をbackup/YYYYMMDD/にコピー - 7日より前のバックアップフォルダは削除
サンプルコード
from pathlib import Path
import shutil
from datetime import datetime, timedelta
base = Path("backup")
today_str = datetime.now().strftime("%Y%m%d")
src_dir = Path("data")
dst_dir = base / today_str
# 今日のバックアップ作成
dst_dir.mkdir(parents=True, exist_ok=True)
shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)
# 古いバックアップ削除(7日以前)
limit_date = datetime.now() - timedelta(days=7)
for d in base.iterdir():
if d.is_dir():
try:
dt = datetime.strptime(d.name, "%Y%m%d")
except ValueError:
continue # 変な名前のディレクトリはスキップ
if dt < limit_date:
shutil.rmtree(d)
print(f"削除: {d}")
ポイント:
-
Path.iterdir()でフォルダ一覧取得 - フォルダ名を日付としてパース
-
shutil.copytree+shutil.rmtreeでコピー&削除
6. どう使い分ければいい?
ざっくり表にするとこんな感じ:
| 操作 | 推奨API | 補足 |
|---|---|---|
| ファイルコピー | shutil.copy2(src, dst) |
メタ情報も保存したいなら copy2
|
| フォルダコピー | shutil.copytree(src_dir, dst_dir) |
既存フォルダに上書きなら dirs_exist_ok=True
|
| ファイル移動 | shutil.move(src, dst) |
ドライブ跨ぎも含めて安心 |
| ちょっとしたリネーム | Path.rename(new_path) |
同一ファイルシステム前提 |
| 単一ファイル削除 | Path.unlink() |
ファイル専用 |
| 空フォルダ削除 | Path.rmdir() |
空でないとエラー |
| フォルダまるごと削除 | shutil.rmtree(path) |
破壊力あるので要注意 |
まとめ
- パスの扱いは pathlib.Path に寄せる(/ 演算子・親ディレクトリ・名前操作が楽)
- コピー・移動・削除など「中身をいじる系」は
shutil+ 一部Pathメソッドを使う -
copy2/copytree/move/rmtreeあたりを覚えておくと、大抵のバッチ処理やツールが書ける