Python講座 / ファイル一括コピーを事前確認と照合付きで行う
ファイルの一括処理では、変更予定を確認する段階と実行する段階を分けます。この実習はテキストファイルを新しい場所へコピーし、同名の出力が既にある場合は上書きせず停止します。
対象範囲と保存方針を決める
新しい練習フォルダーを使います。対象はcopy-demo/inからcopy-demo/outへのコピーで、サブフォルダーを再帰検索せず、シンボリックリンクの項目も除きます。元のファイルは移動・削除しません。
移動は入力側も変更します。コピー、内容照合、後の片付けを分けると、失敗したときの状態を判断しやすくなります。同名の出力に対して無断で別名を作るのではなく、この例は競合として止めます。
サンプルの準備とコピーを分けます。空の練習フォルダーでsetup_copy.pyを一度実行し、copy-demo/in/note.txtを作ります。この準備は書き込みますが、コピーの既定のdry-runはパスを表示するだけで出力フォルダーもファイルも作りません。--applyで初めてコピーし、既存のコピー先があれば停止します。元ファイルの移動・削除は行いません。
全体を setup_copy.py に保存し、同じ練習フォルダーで python setup_copy.py を実行します。
from pathlib import Path
source = Path("copy-demo/in")
source.mkdir(parents=True, exist_ok=True)
with (source / "note.txt").open("x", encoding="utf-8") as f:
f.write("demo\n")
print("sample ready")
sample readyを確認してからsafe_copy.pyを実行します。
コピー先と失敗処理は、try・except・else・finally・raiseで例外を扱うで扱う規則とつながります。
既定の事前確認を実行する
safe_copy.pyをUTF-8で保存し、先に別の準備段階を実行します。
import argparse
import hashlib
import shutil
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("--apply", action="store_true")
args = parser.parse_args()
source, target = Path("copy-demo/in"), Path("copy-demo/out")
if not source.is_dir():
parser.error("prepare copy-demo/in first")
files = sorted(p for p in source.glob("*.txt") if p.is_file() and not p.is_symlink())
for src in files:
dst = target / src.name
if dst.exists() or dst.is_symlink():
raise FileExistsError(dst)
print("copy", src.name)
if args.apply:
target.mkdir(parents=True, exist_ok=True)
with src.open("rb") as incoming, dst.open("xb") as outgoing:
shutil.copyfileobj(incoming, outgoing)
if hashlib.sha256(src.read_bytes()).digest() != hashlib.sha256(dst.read_bytes()).digest():
raise RuntimeError("copy verification failed")
print("applied" if args.apply else "dry-run")
最初の予想結果です。
copy note.txt dry-run
確認したコピーを適用して内容を照合する
対象一覧を確認した後に実行します。
python safe_copy.py --apply
最初はcopy note.txtとappliedが表示され、copy-demo/out/note.txtができます。xbで開くため、既存の出力は拒否します。小さな例ではSHA-256で元と先のバイト列の一致も確認します。
再び実行するとFileExistsErrorになるはずです。コピー失敗では不完全な出力が残る場合があります。削除して再試行する前に、解決済みの絶対パスと内容から、そのファイルが今回の練習対象か確認してください。
拡張する前に限界を理解する
このハッシュ計算は小さなファイル全体をメモリーへ読みます。大きなファイルには分割読み取りが必要です。バイトの一致だけで権限やすべてのメタデータを検証できず、信頼できない利用者が親ディレクトリーを同時に差し替える状況も防いでいません。
改名や削除を追加するなら、対象一覧、衝突検出、バックアップと復旧を設計します。練習結果の片付けでも、絶対パスが使い捨てのcopy-demo内に収まることを確認し、意図したものだけを扱います。相対パスの範囲はpathlibで確認できます。




