Pythonでファイル・ディレクトリをコピーする方法(shutil.copy, copytree)
Pythonでファイルやディレクトリをコピーする際に使用するshutilモジュールの関数について、実用的なサンプルコードと共に詳しく解説します。
ファイルコピーの基本
shutil.copy() – 基本的なファイルコピー
import shutil
# ファイルをコピー(メタデータは保持されない)
shutil.copy('source.txt', 'destination.txt')
shutil.copy2() – メタデータを保持するコピー
import shutil
# ファイルをコピー(タイムスタンプなどのメタデータも保持)
shutil.copy2('source.txt', 'backup/source.txt')
shutil.copyfile() – ファイル内容のみコピー
import shutil
# ファイル内容のみコピー(権限情報は引き継がれない)
shutil.copyfile('source.txt', 'copy_only_content.txt')
ディレクトリのコピー
shutil.copytree() – ディレクトリ全体のコピー
import shutil
# ディレクトリとその中身を再帰的にコピー
shutil.copytree('source_folder', 'destination_folder')
既存ディレクトリへのコピー(Python 3.8以降)
import shutil
# 既存のディレクトリにファイルを追加コピー
shutil.copytree('source', 'existing_dest', dirs_exist_ok=True)
エラーハンドリングを含む安全なコピー
基本的なエラーハンドリング
import shutil
import os
def safe_copy(src, dst):
try:
if not os.path.exists(src):
return f"エラー: '{src}' が存在しません"
# 移動先ディレクトリを作成
dst_dir = os.path.dirname(dst)
if dst_dir and not os.path.exists(dst_dir):
os.makedirs(dst_dir)
if os.path.isfile(src):
shutil.copy2(src, dst)
return f"ファイルコピー完了: '{dst}'"
elif os.path.isdir(src):
shutil.copytree(src, dst, dirs_exist_ok=True)
return f"ディレクトリコピー完了: '{dst}'"
except PermissionError:
return "エラー: 権限がありません"
except shutil.Error as e:
return f"コピーエラー: {e}"
except Exception as e:
return f"予期しないエラー: {e}"
# 使用例
print(safe_copy('test.txt', 'backup/test.txt'))
同名ファイル対応のコピー
import os
import shutil
def copy_with_rename(src, dst):
if os.path.exists(dst):
base, ext = os.path.splitext(dst)
counter = 1
while os.path.exists(f"{base}_copy{counter}{ext}"):
counter += 1
dst = f"{base}_copy{counter}{ext}"
shutil.copy2(src, dst)
return dst
# 使用例
new_path = copy_with_rename('file.txt', 'backup/file.txt')
print(f"コピー先: {new_path}")
条件指定でのファイルコピー
ファイル拡張子による選択コピー
import os
import shutil
import glob
def copy_by_extension(src_dir, dst_dir, extensions):
os.makedirs(dst_dir, exist_ok=True)
copied_files = []
for ext in extensions:
pattern = os.path.join(src_dir, f"*.{ext}")
files = glob.glob(pattern)
for file in files:
filename = os.path.basename(file)
dst_path = os.path.join(dst_dir, filename)
shutil.copy2(file, dst_path)
copied_files.append(filename)
return copied_files
# 使用例:txt, pdf ファイルのみコピー
copied = copy_by_extension('documents', 'backup', ['txt', 'pdf'])
print(f"コピーしたファイル: {copied}")
サイズ制限付きコピー
import os
import shutil
def copy_small_files(src_dir, dst_dir, max_size_mb=10):
os.makedirs(dst_dir, exist_ok=True)
max_bytes = max_size_mb * 1024 * 1024
copied_count = 0
for filename in os.listdir(src_dir):
src_path = os.path.join(src_dir, filename)
if os.path.isfile(src_path):
if os.path.getsize(src_path) <= max_bytes:
dst_path = os.path.join(dst_dir, filename)
shutil.copy2(src_path, dst_path)
copied_count += 1
return copied_count
# 使用例:10MB以下のファイルのみコピー
count = copy_small_files('source', 'small_files_backup', 10)
print(f"{count}個のファイルをコピーしました")
高度なコピー操作
進捗表示付きコピー
import shutil
import os
def copy_with_progress(src, dst):
if os.path.isfile(src):
file_size = os.path.getsize(src)
def progress_callback():
print(".", end="", flush=True)
print(f"コピー中: {src}")
shutil.copy2(src, dst)
print(f"\nコピー完了: {dst}")
elif os.path.isdir(src):
print(f"ディレクトリコピー中: {src}")
def copy_function(src_file, dst_file):
shutil.copy2(src_file, dst_file)
print(".", end="", flush=True)
shutil.copytree(src, dst, copy_function=copy_function)
print(f"\nディレクトリコピー完了: {dst}")
# 使用例
copy_with_progress('large_folder', 'backup_folder')
選択的ディレクトリコピー
import shutil
import os
def selective_copytree(src, dst, ignore_patterns=None):
ignore_patterns = ignore_patterns or []
def ignore_function(directory, contents):
ignored = []
for item in contents:
for pattern in ignore_patterns:
if pattern in item:
ignored.append(item)
break
return ignored
shutil.copytree(src, dst, ignore=ignore_function)
return f"選択的コピー完了: {dst}"
# 使用例:.pyc ファイルと __pycache__ を除外
result = selective_copytree(
'project',
'project_backup',
ignore_patterns=['.pyc', '__pycache__', '.git']
)
print(result)
差分コピー(新しいファイルのみ)
import os
import shutil
def incremental_copy(src_dir, dst_dir):
os.makedirs(dst_dir, exist_ok=True)
new_files = []
updated_files = []
for root, dirs, files in os.walk(src_dir):
for file in files:
src_path = os.path.join(root, file)
rel_path = os.path.relpath(src_path, src_dir)
dst_path = os.path.join(dst_dir, rel_path)
# 移動先ディレクトリを作成
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
if not os.path.exists(dst_path):
shutil.copy2(src_path, dst_path)
new_files.append(rel_path)
elif os.path.getmtime(src_path) > os.path.getmtime(dst_path):
shutil.copy2(src_path, dst_path)
updated_files.append(rel_path)
return {
'new_files': new_files,
'updated_files': updated_files
}
# 使用例
result = incremental_copy('source', 'backup')
print(f"新規: {len(result['new_files'])}個")
print(f"更新: {len(result['updated_files'])}個")
コピー操作の検証
ハッシュ値による整合性チェック
import shutil
import hashlib
import os
def copy_and_verify(src, dst):
def get_file_hash(filepath):
with open(filepath, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
if os.path.isfile(src):
original_hash = get_file_hash(src)
shutil.copy2(src, dst)
copied_hash = get_file_hash(dst)
if original_hash == copied_hash:
return f"コピー成功: ハッシュ値一致 ({dst})"
else:
return f"コピーエラー: ハッシュ値不一致"
else:
shutil.copytree(src, dst)
return f"ディレクトリコピー完了: {dst}"
# 使用例(バイナリファイルで使用)
# result = copy_and_verify('image.jpg', 'backup/image.jpg')
# print(result)
ディスク容量チェック付きコピー
import shutil
import os
def copy_with_space_check(src, dst):
def get_size(path):
if os.path.isfile(path):
return os.path.getsize(path)
else:
total = 0
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
fp = os.path.join(dirpath, filename)
total += os.path.getsize(fp)
return total
required_space = get_size(src)
dst_dir = os.path.dirname(dst) if os.path.isfile(src) else dst
available_space = shutil.disk_usage(dst_dir).free
if required_space > available_space:
return f"容量不足: 必要 {required_space}, 利用可能 {available_space}"
if os.path.isfile(src):
shutil.copy2(src, dst)
else:
shutil.copytree(src, dst)
return f"コピー完了: {dst}"
# 使用例
print(copy_with_space_check('large_file.zip', 'backup/large_file.zip'))
バックアップシステムの実装
日付付きバックアップ
import shutil
import os
from datetime import datetime
def create_dated_backup(src, backup_root):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
if os.path.isfile(src):
filename = os.path.basename(src)
name, ext = os.path.splitext(filename)
backup_name = f"{name}_{timestamp}{ext}"
dst = os.path.join(backup_root, backup_name)
shutil.copy2(src, dst)
else:
folder_name = os.path.basename(src.rstrip('/\\'))
backup_name = f"{folder_name}_{timestamp}"
dst = os.path.join(backup_root, backup_name)
shutil.copytree(src, dst)
return dst
# 使用例
backup_path = create_dated_backup('important.txt', 'backups')
print(f"バックアップ作成: {backup_path}")
まとめ
Pythonでのファイル・ディレクトリコピーには以下の関数を使い分けます:
shutil.copy(): 基本的なファイルコピーshutil.copy2(): メタデータ保持ファイルコピーshutil.copyfile(): ファイル内容のみコピーshutil.copytree(): ディレクトリの再帰コピー
エラーハンドリング、進捗表示、選択的コピー機能を組み合わせることで、robust で使いやすいバックアップシステムを構築できます。
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座

