Pythonのファイル・ディレクトリ操作をスマートに!「shutil」で効率アップ!

投稿者: | 2026-04-02

ファイルやディレクトリの操作は、プログラミングにおいて避けて通れない基本的なタスクですよね。Pythonには、これらの操作を行うための強力な標準ライブラリがいくつかありますが、中でも特に便利なのが今回ご紹介する「shutil」モジュールです。

「shutil」とは?

shutilは「shell utilities(シェルユーティリティ)」の略で、高レベルのファイル操作をサポートするPythonの標準ライブラリです。osモジュールでもファイル操作は可能ですが、例えばディレクトリごとコピーしたり、圧縮・展開したりといった、より複雑な操作を、shutilはたった1つの関数で簡単に実現してくれます。

「shutil」を使うメリット

shutilモジュールを活用することで、以下のようなメリットがあります。

  • 高レベルな操作を簡単に実現: ディレクトリツリー全体のコピーや削除、ファイルやディレクトリの移動、アーカイブ(圧縮)ファイルの作成や展開など、複数ステップが必要な操作をシンプルに記述できます。
  • コードの簡潔さと可読性の向上: 手動で複雑なロジックを組む必要がなくなり、コードが短く、読みやすくなります。これにより、開発効率も向上します。
  • OS間の互換性: 「shutil」は、基盤となるOSの違いを吸収してくれるため、Windows、macOS、Linuxなど、異なる環境でも同じコードでファイル操作が可能です。
  • エラーハンドリングの簡素化: ファイル操作中に発生しがちな権限エラーやパスエラーなども、「shutil」が適切に処理し、必要に応じて詳細なエラー情報を返してくれるため、安定したコードを書きやすくなります。

サンプルコードで学ぶ`shutil`の使い方

それでは、実際にshutilモジュールの便利な機能を見ていきましょう。

以下のコードを実行する前に、テスト用の環境を準備します。

import os
import shutil

# テスト用ディレクトリとファイルを作成
if not os.path.exists("source_dir"):
    os.makedirs("source_dir")
with open(os.path.join("source_dir", "test_file.txt"), "w") as f:
    f.write("Hello, shutil!")
if not os.path.exists(os.path.join("source_dir", "sub_dir")):
    os.makedirs(os.path.join("source_dir", "sub_dir"))
with open(os.path.join("source_dir", "sub_dir", "sub_file.txt"), "w") as f:
    f.write("This is a sub file.")
if not os.path.exists("target_dir"):
    os.makedirs("target_dir")

print("--- 準備完了 ---")
print(f"作成されたディレクトリとファイル:\n  source_dir/\n    test_file.txt\n    sub_dir/\n      sub_file.txt\n  target_dir/")

1. ファイルのコピー: 「shutil.copy()」

指定したファイルを別の場所へコピーします。コピー先がディレクトリの場合、ファイルはそのディレクトリ内にコピーされます。

# target_dirが存在することを確認
if not os.path.exists("target_dir"):
    os.makedirs("target_dir")

shutil.copy(os.path.join("source_dir", "test_file.txt"), "target_dir")
print(f"'{os.path.join('source_dir', 'test_file.txt')}'を'target_dir'にコピーしました。")
print("target_dirの内容:", os.listdir("target_dir"))
# 出力例: target_dirの内容: ['test_file.txt']

2. ディレクトリのコピー: 「shutil.copytree()」

ディレクトリとその中にある全てのファイル、サブディレクトリを丸ごとコピーします。コピー先のディレクトリが存在しない必要があります。

if os.path.exists("copied_source_dir"):
    shutil.rmtree("copied_source_dir") # 既存の場合は削除

shutil.copytree("source_dir", "copied_source_dir")
print(f"'source_dir'を'copied_source_dir'にコピーしました。")
print("copied_source_dirの内容:", os.listdir("copied_source_dir"))
print("copied_source_dir/sub_dirの内容:", os.listdir(os.path.join("copied_source_dir", "sub_dir")))
# 出力例:
# copied_source_dirの内容: ['sub_dir', 'test_file.txt']
# copied_source_dir/sub_dirの内容: ['sub_file.txt']

3. ファイルやディレクトリの移動: 「shutil.move()」

ファイルやディレクトリを移動します。移動先が既存のディレクトリの場合、そのディレクトリの中に移動されます。ファイル名を変えたい場合は、移動先のパスに新しいファイル名を指定します。

# test_file.txtをtarget_dirからmoved_file.txtとして移動
shutil.move(os.path.join("target_dir", "test_file.txt"), os.path.join("target_dir", "moved_file.txt"))
print(f"'{os.path.join('target_dir', 'test_file.txt')}'を'{os.path.join('target_dir', 'moved_file.txt')}'に移動しました。")
print("target_dirの内容:", os.listdir("target_dir"))
# 出力例: target_dirの内容: ['moved_file.txt']

# source_dir/sub_dirを別の場所に移動
if not os.path.exists("another_target_dir"):
    os.makedirs("another_target_dir")
shutil.move(os.path.join("source_dir", "sub_dir"), "another_target_dir")
print(f"'{os.path.join('source_dir', 'sub_dir')}'を'another_target_dir'に移動しました。")
print("source_dirの内容:", os.listdir("source_dir"))
print("another_target_dirの内容:", os.listdir("another_target_dir"))
# 出力例:
# source_dirの内容: ['test_file.txt']
# another_target_dirの内容: ['sub_dir']

4. ディレクトリの削除: 「shutil.rmtree()」

ディレクトリとその中身を全て削除します。空でないディレクトリも一括で削除できるため、非常に強力ですが、その分使用には注意が必要です。

if os.path.exists("copied_source_dir"):
    shutil.rmtree("copied_source_dir")
    print("'copied_source_dir'を削除しました。")
else:
    print("'copied_source_dir'は既に存在しません。")
# 出力例: 'copied_source_dir'を削除しました。

私も初めて使った時、勢い余って大事なファイルを消しそうになったことがあります。使う前には、必ずパスが正しいか確認しましょうね!

5. アーカイブの作成: 「shutil.make_archive()」

指定したディレクトリの内容をzipやtarなどの形式で圧縮します。

# 'my_archive.zip'という名前でsource_dirをZIP形式で圧縮
archive_path = shutil.make_archive("my_archive", 'zip', "source_dir")
print(f"'source_dir'をアーカイブしました: {archive_path}")
print("現在のディレクトリのファイル:", [f for f in os.listdir(".") if f.startswith("my_archive")])
# 出力例:
# 'source_dir'をアーカイブしました: my_archive.zip
# 現在のディレクトリのファイル: ['my_archive.zip']

6. アーカイブの展開: 「shutil.unpack_archive()」

圧縮されたアーカイブファイルを指定したディレクトリに展開します。

if os.path.exists("extracted_dir"):
    shutil.rmtree("extracted_dir")
os.makedirs("extracted_dir") # 展開先のディレクトリを作成

shutil.unpack_archive(archive_path, "extracted_dir")
print(f"'{archive_path}'を'extracted_dir'に展開しました。")
print("extracted_dirの内容:", os.listdir("extracted_dir"))
print("extracted_dir/sub_dirの内容:", os.listdir(os.path.join("extracted_dir", "sub_dir")))
# 出力例:
# 'my_archive.zip'を'extracted_dir'に展開しました。
# extracted_dirの内容: ['test_file.txt', 'sub_dir']
# extracted_dir/sub_dirの内容: ['sub_file.txt']

7. クリーンアップ

テストのために作成したファイルやディレクトリを削除します。

# 作成したテストファイルを全て削除
if os.path.exists("source_dir"):
    shutil.rmtree("source_dir")
if os.path.exists("target_dir"):
    shutil.rmtree("target_dir")
if os.path.exists("another_target_dir"):
    shutil.rmtree("another_target_dir")
if os.path.exists("extracted_dir"):
    shutil.rmtree("extracted_dir")
if os.path.exists(archive_path):
    os.remove(archive_path)

print("--- クリーンアップ完了 ---")

shutilモジュールを使うと、本当に簡単にファイルやディレクトリを操作できることがお分かりいただけたでしょうか。これらの関数を組み合わせることで、より複雑なファイル管理スクリプトもスマートに記述できますよ!

Pythonの標準ライブラリだけでここまでできるなんて、本当にすごいですよね!ファイル整理のスクリプトなんかも、これでサッと作れちゃいます。

みーちゃんのワンポイント

shutil.rmtree() は強力な削除機能なので、誤って重要なファイルを消してしまわないよう、パスの指定には細心の注意を払いましょう。 また、shutil.copytree() で既存のディレクトリに上書きしたい場合は、Python 3.8以降で追加された dirs_exist_ok=True オプションを忘れずに指定してくださいね。