閉じる

Pythonでファイル/ディレクトリの存在確認・削除・一覧取得について解説

日々のスクリプトや自動化では、ファイルやディレクトリの存在確認、一覧取得、削除の3つが基礎になります。

本記事ではPython初心者の方に向けて、標準ライブラリのみでできる安全で実用的な方法を体系的に解説します。

os系とpathlib系の両アプローチを比較しながら、例外処理や誤削除防止のコツまで丁寧に説明します。

ファイル/ディレクトリの存在確認

存在確認の基本は「何を知りたいか」で使い分けることです。

ファイルがあるか、ディレクトリかどうか、シンボリックリンクをどう扱うかで適切な関数が変わります。

初心者の方は、まずはos.pathpathlib.Pathの両方での書き方を覚えるとよいです。

os.path.exists/isfile/isdirの使い分け

os.pathは従来からある手堅いAPIです。

違いをまとめると次のようになります。

関数返り値主な用途注意点
os.path.exists(path)True/Falseパスが存在するか(ファイル/ディレクトリ/リンク含む)壊れたシンボリックリンクはFalseになります(lexistsならTrue)
os.path.isfile(path)True/False通常ファイルかどうかを判定ディレクトリや壊れたリンクはFalse
os.path.isdir(path)True/Falseディレクトリかどうかを判定ファイルや壊れたリンクはFalse

壊れたシンボリックリンクも存在として扱いたい場合はos.path.lexists(path)を使います。

実例(一時ディレクトリで安全に検証)

Python
# exists, isfile, isdir の違いを確認するデモ
# 一時ディレクトリ内でファイルとサブディレクトリを作成して判定します

import os
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    file_path = os.path.join(tmp, "data.txt")
    dir_path = os.path.join(tmp, "subdir")

    # テスト用ファイルとディレクトリを作成
    with open(file_path, "w", encoding="utf-8") as f:
        f.write("hello")
    os.mkdir(dir_path)

    print("tmp =", tmp)
    print("exists(file):", os.path.exists(file_path))  # True
    print("isfile(file):", os.path.isfile(file_path))  # True
    print("isdir(file):", os.path.isdir(file_path))    # False
    print("exists(dir):", os.path.exists(dir_path))    # True
    print("isfile(dir):", os.path.isfile(dir_path))    # False
    print("isdir(dir):", os.path.isdir(dir_path))      # True

    # 存在しないパス
    missing = os.path.join(tmp, "missing.txt")
    print("exists(missing):", os.path.exists(missing)) # False
実行結果
tmp = /tmp/tmpabcd1234
exists(file): True
isfile(file): True
isdir(file): False
exists(dir): True
isfile(dir): False
isdir(dir): True
exists(missing): False

pathlib.Path.exists/is_file/is_dirの使い分け

pathlibはパスをオブジェクトとして扱えるモダンAPIです。

可読性が高く、パス結合や走査が直感的なので積極的に使うことをおすすめします。

メソッド返り値主な用途注意点
Path.exists()True/Falseパスが存在するか壊れたシンボリックリンクはFalse
Path.is_file()True/False通常ファイルかどうかディレクトリはFalse
Path.is_dir()True/FalseディレクトリかどうかファイルはFalse
Python
# pathlib 版の存在確認デモ
from pathlib import Path
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    base = Path(tmp)
    p_file = base / "note.txt"
    p_dir = base / "docs"

    p_file.write_text("memo", encoding="utf-8")
    p_dir.mkdir()

    print("base =", base)
    print("exists(file):", p_file.exists())  # True
    print("is_file(file):", p_file.is_file())  # True
    print("is_dir(file):", p_file.is_dir())    # False
    print("exists(dir):", p_dir.exists())    # True
    print("is_file(dir):", p_dir.is_file())  # False
    print("is_dir(dir):", p_dir.is_dir())    # True

    p_missing = base / "none.txt"
    print("exists(missing):", p_missing.exists())  # False
実行結果
base = /tmp/tmpwxyz5678
exists(file): True
is_file(file): True
is_dir(file): False
exists(dir): True
is_file(dir): False
is_dir(dir): True
exists(missing): False

例外(FileNotFoundError)との違いと注意

「存在確認してから操作」には競合状態(TOCTOU)の落とし穴があります

確認した直後に別プロセスが削除し、操作時にFileNotFoundErrorになることがあるためです。

実運用では事前チェックより「やってみて例外を捕まえる」方式が堅牢です。

Python
# よくあるNGパターン: 先にexistsで確認してから開く
# 直後に別プロセスが削除した場合、openで FileNotFoundError になる可能性があります
from pathlib import Path

p = Path("maybe_deleted.txt")
if p.exists():  # ここでTrueでも、この直後に削除され得る
    try:
        with p.open("r", encoding="utf-8") as f:
            data = f.read()
    except FileNotFoundError:
        print("消えていました")  # 競合に弱い
Python
# 推奨パターン: 例外処理で包む(競合に強い)
from pathlib import Path

p = Path("maybe_deleted.txt")
try:
    with p.open("r", encoding="utf-8") as f:
        data = f.read()
except FileNotFoundError:
    print("そもそも存在しない/直前に削除されたため開けませんでした")

存在確認は「UI表示や分岐のための目安」に留め、実操作は例外処理で守るのが安全です。

ファイル/ディレクトリの一覧取得

一覧取得は用途に応じて使い分けます。

名前だけほしいならos.listdir、属性も含め効率よく取得したいならos.scandir、直感的なパス操作をしたいならpathlib.Path.iterdir、パターンで探すならglob/rglobが基本です。

os.listdirで一覧取得(名前のみ)

os.listdir(path)は指定ディレクトリ直下の「名前」だけを返します。

サブディレクトリの中身は含みません。

Python
# os.listdir の基本。直下のエントリ名のみを取得します。
import os
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    open(os.path.join(tmp, "a.txt"), "w").close()
    os.mkdir(os.path.join(tmp, "images"))
    open(os.path.join(tmp, "b.log"), "w").close()

    print("listdir:", os.listdir(tmp))  # 並び順は環境依存
実行結果
listdir: ['a.txt', 'images', 'b.log']

os.scandirで効率的に取得(属性付き)

os.scandir(path)DirEntryオブジェクトを返します。

型判定(is_file(), is_dir())やサイズ(stat())を必要に応じて遅延取得できるため、大量の項目を扱う場合に効率的です。

Python
# os.scandir で名前・種別・サイズを表示する例
import os
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    # サンプルデータ
    with open(os.path.join(tmp, "readme.txt"), "w", encoding="utf-8") as f:
        f.write("Hello\n")
    os.mkdir(os.path.join(tmp, "data"))
    with open(os.path.join(tmp, "data.bin"), "wb") as f:
        f.write(b"\x00" * 10)

    with os.scandir(tmp) as it:
        for entry in it:
            kind = "dir" if entry.is_dir() else ("file" if entry.is_file() else "other")
            size = entry.stat().st_size if entry.is_file() else "-"
            print(f"{entry.name:12}  {kind:4}  size={size}")
実行結果
readme.txt   file  size=6
data         dir   size=-
data.bin     file  size=10

pathlib.Path.iterdirで直下を列挙

Path.iterdir()は直下のPathオブジェクトを生成します。

パス結合やフィルタをメソッドチェーンで書きやすいのが利点です。

Python
# pathlib の iterdir で直下の Path を列挙
from pathlib import Path
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    p = Path(tmp)
    (p / "doc.md").write_text("# title\n", encoding="utf-8")
    (p / "out").mkdir()
    (p / "notes.txt").write_text("n", encoding="utf-8")

    for child in p.iterdir():
        print(child.name, "->", "dir" if child.is_dir() else "file")
実行結果
doc.md -> file
out -> dir
notes.txt -> file

glob/rglobでパターン検索

ファイル名のパターン(ワイルドカード)で探すにはglobが便利です。

**を使うと再帰的検索ができます。

Python
# glob モジュールの基本と再帰検索
import glob
import os
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    open(os.path.join(tmp, "a.txt"), "w").close()
    open(os.path.join(tmp, "b.log"), "w").close()
    os.mkdir(os.path.join(tmp, "sub"))
    open(os.path.join(tmp, "sub", "c.txt"), "w").close()

    print("*.txt:", sorted(glob.glob(os.path.join(tmp, "*.txt"))))
    print("**/*.txt (recursive):", sorted(glob.glob(os.path.join(tmp, "**", "*.txt"), recursive=True)))
実行結果
*.txt: ['/tmp/tmpq1/a.txt']
**/*.txt (recursive): ['/tmp/tmpq1/a.txt', '/tmp/tmpq1/sub/c.txt']

pathlibでも同様にPath.glob()Path.rglob()が使えます。

Python
# pathlib の glob / rglob
from pathlib import Path
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    base = Path(tmp)
    (base / "x1.py").write_text("print(1)\n", encoding="utf-8")
    (base / "pkg").mkdir()
    (base / "pkg" / "x2.py").write_text("print(2)\n", encoding="utf-8")

    print("glob('*.py'):", [p.name for p in base.glob("*.py")])
    print("rglob('*.py'):", sorted(p.name for p in base.rglob("*.py")))
実行結果
glob('*.py'): ['x1.py']
rglob('*.py'): ['x1.py', 'x2.py']

ファイルの削除

ファイル削除は「小さな操作でも不可逆」です。

取り消しはできないため、例外処理やドライラン(予行演習)の併用を検討してください。

os.removeとpathlib.Path.unlinkの基本

ファイル削除はos.remove(path)またはPath.unlink()を使います。

どちらも通常ファイルに対して動作し、存在しなければFileNotFoundError、権限がなければPermissionErrorが発生します。

Python
# ファイル削除の基本(存在するファイルを削除)
import os
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    target = os.path.join(tmp, "temp.txt")
    open(target, "w").close()
    print("before exists:", os.path.exists(target))  # True
    os.remove(target)  # または pathlib.Path(target).unlink()
    print("after exists :", os.path.exists(target))  # False
実行結果
before exists: True
after exists : False

削除前の存在確認と例外処理

削除も「やってみて例外を捕まえる」方式が堅牢です。

事前にexists()で確認しても、直後に他プロセスが削除している可能性があります。

Python
# 推奨: 例外を捕まえて安全に削除する関数
from pathlib import Path

def remove_file_safe(path: Path) -> bool:
    """
    指定ファイルを削除する。成功: True, 既にない: False を返す。
    PermissionError などは上位に伝播(ログに残すなど)。
    """
    try:
        path.unlink()
        return True
    except FileNotFoundError:
        return False  # 既にない
    # except IsADirectoryError:  # ディレクトリに対して呼ぶと出る
    #     ... (用途に応じて処理)

# デモ
if __name__ == "__main__":
    import tempfile
    with tempfile.TemporaryDirectory() as tmp:
        p = Path(tmp) / "to_delete.txt"
        p.write_text("x", encoding="utf-8")
        print("deleted:", remove_file_safe(p))       # True
        print("deleted again:", remove_file_safe(p)) # False
実行結果
deleted: True
deleted again: False

補足として、Windowsでは開いているファイルは削除できずPermissionErrorになります(Linuxでは削除は可能だが参照は残る)。

環境差も考慮して例外処理でカバーすると安心です。

ディレクトリの削除

ディレクトリ削除は空かどうかで使う関数が異なります。

空ならrmdir、中身ごとならshutil.rmtreeが基本です。

空ディレクトリ削除(os.rmdir/Path.rmdir)

空のディレクトリはos.rmdir(path)またはPath(path).rmdir()で削除します。

中身があるとOSError(具体的にはNotEmpty)になります。

Python
# 空ディレクトリの削除
import os
from pathlib import Path
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    empty_dir = Path(tmp) / "empty"
    empty_dir.mkdir()
    print("exists before:", empty_dir.exists())  # True
    empty_dir.rmdir()  # os.rmdir(str(empty_dir)) でも可
    print("exists after :", empty_dir.exists())  # False
実行結果
exists before: True
exists after : False

中身ごと削除(shutil.rmtree)

中身があるディレクトリはshutil.rmtree(path)で再帰的に削除します。

非常に強力で取り消し不能なので細心の注意が必要です。

Python
# ディレクトリを中身ごと削除
from pathlib import Path
import shutil
import tempfile

with tempfile.TemporaryDirectory() as tmp:
    base = Path(tmp) / "project"
    (base / "src").mkdir(parents=True)
    (base / "src" / "main.py").write_text("print('hi')\n", encoding="utf-8")
    (base / "data").mkdir()
    (base / "data" / "input.txt").write_text("data\n", encoding="utf-8")

    print("exists before:", base.exists())  # True
    shutil.rmtree(base)  # 中身ごと消える
    print("exists after :", base.exists())  # False
実行結果
exists before: True
exists after : False

注意点として、ディレクトリを指すシンボリックリンクをrmtreeに渡すと、リンク自体を削除し、リンク先は削除しません(通常の安全な挙動)。

特殊なファイル(デバイスファイル等)や権限の制限があると例外になります。

誤削除防止のチェック(ドライラン/対象絞り込み)

本番でrmtreeを実行する前に「何が消えるか」を確認するドライランが有効です。

また、許可されたディレクトリ配下だけを対象にすることでパス指定ミスを減らせます。

Python
# ドライラン付きで安全に削除するユーティリティ
from pathlib import Path
import shutil

def is_relative_to(child: Path, parent: Path) -> bool:
    """Python 3.9未満互換: child が parent 配下なら True"""
    try:
        child.resolve().relative_to(parent.resolve())
        return True
    except Exception:
        return False

def remove_tree_safe(target: Path, allow_base: Path, dry_run: bool = True) -> None:
    """
    allow_base 配下の target だけを削除対象とし、dry_run=True のときは一覧を表示して削除しない。
    """
    if not is_relative_to(target, allow_base):
        raise ValueError(f"危険: {target} は許可ベース {allow_base} の配下ではありません")

    if not target.exists():
        print(f"[info] {target} は存在しません")
        return

    if dry_run:
        print("[DRY-RUN] 次を削除予定:")
        for p in sorted(target.rglob("*"), key=lambda x: (x.is_file(), str(x))):
            kind = "DIR" if p.is_dir() else "FILE"
            print(f"  {kind}: {p}")
        print("[DRY-RUN] 実削除は行っていません")
    else:
        shutil.rmtree(target)
        print(f"[done] 削除しました: {target}")

# デモ
if __name__ == "__main__":
    import tempfile
    with tempfile.TemporaryDirectory() as tmp:
        base = Path(tmp)
        t = base / "sandbox"
        (t / "logs").mkdir(parents=True)
        (t / "logs" / "app.log").write_text("log\n", encoding="utf-8")
        (t / "data").mkdir()
        (t / "data" / "1.txt").write_text("1\n", encoding="utf-8")

        # まずドライラン
        remove_tree_safe(t, allow_base=base, dry_run=True)
        # 次に本当に削除
        remove_tree_safe(t, allow_base=base, dry_run=False)
実行結果
[DRY-RUN] 次を削除予定:
  DIR: /tmp/tmpabcd/sandbox/data
  FILE: /tmp/tmpabcd/sandbox/data/1.txt
  DIR: /tmp/tmpabcd/sandbox/logs
  FILE: /tmp/tmpabcd/sandbox/logs/app.log
[DRY-RUN] 実削除は行っていません
[done] 削除しました: /tmp/tmpabcd/sandbox

絶対パスの誤指定(例: /C:\)は壊滅的です

上記のようなベース配下チェックや、対象件数・合計サイズの表示、ユーザー確認(Yes/No)を併用すると安全性が高まります。

まとめ

本記事では、Python標準ライブラリを用いた存在確認・一覧取得・削除を整理しました。

用途に応じて適切なAPIを選び、実操作は例外処理で守ることが最大のポイントです。

特に削除処理は取り返しがつかないため、ドライラン・ベース配下チェック・ユーザー確認などの安全策を取りましょう。

実務ではpathlibの表現力が役立ちますが、os系APIの挙動も理解しておくと既存コードの読解や移行がスムーズです。

今後は本記事を土台に、ログ回収やバックアップスクリプトなど、信頼性の高い自動化に発展させていってください。

Python 基本的なファイル操作
この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!