概要(shutil.copy は「ファイルを別の場所へコピーする」高レベル関数)
shutil.copy は、指定した1つの“ファイル”を別のパスへコピーする標準ライブラリの関数です。コピー先が「ファイル名」ならその名前で書き出し、「ディレクトリ」なら元ファイル名のままその中へコピーします。中身(内容)は必ずコピーされますが、タイムスタンプなどのメタデータは基本的に完全には引き継がれません。重要なのは、コピー先ディレクトリの存在確認、権限、上書きの設計、そして例外対策です。
import shutil
# ファイルを別名でコピー
shutil.copy("report.txt", "backup/report_copy.txt")
# ディレクトリを指定すると中へコピー(同名ファイル)
shutil.copy("report.txt", "backup")
Python基本の使い方(ここが重要)
コピー元・コピー先の指定と挙動の違い
- コピー元(src): 実在する“ファイル”パスを渡す(ディレクトリは不可)。
- コピー先(dst):
- ファイルパスを渡すと、その“指定名”でコピー。
- 既存ディレクトリを渡すと、その“中へ同名”でコピー。
import shutil
# 指定名でコピー(上書き注意)
shutil.copy("data.csv", "out/result.csv")
# ディレクトリへコピー(out/data.csv が作成される)
shutil.copy("data.csv", "out")
Python上書きの扱い(既存ファイルは上書きされる)
dst に既存のファイルを指定すると、同名ファイルは上書きされます。誤上書きを避けたいときは事前に存在チェックや、別名付与・日付付与で回避します。
import os, shutil
dst = "out/result.csv"
if os.path.exists(dst):
raise FileExistsError("既に存在します")
shutil.copy("data.csv", dst)
Python例外対策(存在しない・権限なし・ディレクトリ未作成)
- FileNotFoundError: src が無い、または dst の親ディレクトリが無い。
- PermissionError: 書き込み権限がない、使用中でロックされている等。
- IsADirectoryError: src がディレクトリなど不正。
import os, shutil
def safe_copy(src: str, dst: str) -> bool:
try:
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy(src, dst)
return True
except (FileNotFoundError, PermissionError, IsADirectoryError) as e:
print("COPY ERROR:", e)
return False
Python関連関数との使い分け(重要ポイントの深掘り)
copy と copy2(メタデータをどこまで保つか)
- shutil.copy: 内容をコピー。アクセス権(mode)を可能な範囲で反映。タイムスタンプなどのメタデータは十分に保持されないことがある。
- shutil.copy2: 内容+メタデータ(mtime 等)も可能な範囲でコピー。バックアップや監査向けならこちらが適切。
import shutil
# メタデータもできる限り保つ
shutil.copy2("data.csv", "backup/data.csv")
Pythoncopyfile と copytree(ファイル限定か、ディレクトリ丸ごとか)
- shutil.copyfile(src, dst): ファイル“内容のみ”をコピー。dst はファイルパス必須(ディレクトリ不可)。細かい制御が欲しいときに。
- shutil.copytree(src_dir, dst_dir): ディレクトリを“丸ごと”再帰的にコピー。dst_dir が存在しているとエラー(Python 3.8+ は dirs_exist_ok=True で上書き許可)。
import shutil
# ファイル限定で内容コピー(メタデータは最小限)
shutil.copyfile("config.yaml", "backup/config.yaml")
# ディレクトリ丸ごと(既存ならエラー。3.8+ は引数で許可可)
shutil.copytree("templates", "project_templates")
Pythonmove(コピー+削除=移動)との違い
- shutil.move: コピーして元を削除(同じボリュームではリネームに近い動作)。「複製」ではなく「移動」なので用途が別。
import shutil
shutil.move("old.txt", "archive/old.txt") # これは“移動”。コピーではない
Python実務設計の勘所(安全・再現性・パフォーマンス)
事前にコピー先ディレクトリを用意する
dst をファイルパスで指定するなら、親ディレクトリを作っておくのが安全。なければ os.makedirs(…, exist_ok=True)。
import os, shutil
dst = "backup/2025-12/report.txt"
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy("report.txt", dst)
Python上書きポリシーを決める(重複対策)
業務では“既存なら”どうするかが重要。タイムスタンプや連番を付けて衝突を避ける、既存ならエラーにする、比較(ハッシュ)して同一ならスキップする等、方針を明確に。
import os, shutil
from datetime import datetime
def copy_with_timestamp(src: str, dst_dir: str) -> str:
os.makedirs(dst_dir, exist_ok=True)
base = os.path.splitext(os.path.basename(src))[0]
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
dst = os.path.join(dst_dir, f"{base}-{stamp}.txt")
shutil.copy(src, dst)
return dst
Pythonメタデータが大事なら copy2、丸ごとなら copytree
監査ログやバックアップで“更新時刻”が重要なら copy2。テンプレート一式の複製などは copytree が本筋。単一ファイルなら copy/copy2 を選ぶ。
大量コピーはエラーに強く(1件ずつ try/except、途中経過ログ)
大量のファイルを処理する場合、エラーは必ず混ざる前提で、1件ずつ例外を握りつつ進めると運用が安定します。
例題で身につける(定番から一歩先まで)
例題1:CSV を全てバックアップ(ディレクトリへ同名コピー)
import os, shutil
def backup_csv(src_dir: str, dst_dir: str) -> int:
os.makedirs(dst_dir, exist_ok=True)
count = 0
for name in os.listdir(src_dir):
src = os.path.join(src_dir, name)
if os.path.isfile(src) and name.lower().endswith(".csv"):
shutil.copy(src, dst_dir) # dst_dir に同名でコピー
count += 1
return count
print(backup_csv("data", "backup"))
Python例題2:メタデータ保持でレポートをバックアップ(copy2)
import os, shutil
def backup_reports(src_dir: str, dst_dir: str) -> None:
os.makedirs(dst_dir, exist_ok=True)
for name in os.listdir(src_dir):
src = os.path.join(src_dir, name)
if os.path.isfile(src) and name.endswith(".txt"):
shutil.copy2(src, os.path.join(dst_dir, name))
backup_reports("reports", "backup/reports")
Python例題3:拡張子別にコピー先を分ける(安全な作成と上書き管理)
import os, shutil
def copy_by_ext(src_dir: str, dst_root: str) -> None:
for name in os.listdir(src_dir):
src = os.path.join(src_dir, name)
if os.path.isfile(src):
ext = os.path.splitext(name)[1].lstrip(".").lower() or "_none"
dst_dir = os.path.join(dst_root, ext)
os.makedirs(dst_dir, exist_ok=True)
dst = os.path.join(dst_dir, name)
shutil.copy(src, dst)
copy_by_ext("downloads", "sorted")
Python例題4:差分コピー(同じ内容ならスキップ)
import os, shutil, hashlib
def sha256(path: str, buf: int = 1 << 20) -> str:
h = hashlib.sha256()
with open(path, "rb") as f:
while True:
chunk = f.read(buf)
if not chunk: break
h.update(chunk)
return h.hexdigest()
def copy_if_changed(src: str, dst: str) -> bool:
os.makedirs(os.path.dirname(dst), exist_ok=True)
if os.path.exists(dst):
if sha256(src) == sha256(dst):
return False # 同一ならコピーしない
shutil.copy(src, dst)
return True
copied = copy_if_changed("data.csv", "backup/data.csv")
print("copied:", copied)
Pythonまとめ
shutil.copy は「単一ファイルのコピー」を高レベルに安全に行うための標準関数です。コピー先がファイルかディレクトリかで挙動が変わる点、既存ファイルは上書きされる点、例外(存在・権限・親未作成)への備えが重要。メタデータ保持が必要なら shutil.copy2、ディレクトリ丸ごとは shutil.copytree、内容のみなら shutil.copyfile を使い分ける。コピー先の事前作成、上書きポリシー、差分判定などを設計に組み込めば、初心者でも堅牢で実務品質の“コピー処理”を書けます。
