note.nkmk.me

Pythonでファイル・ディレクトリを削除するos.remove, shutil.rmtreeなど

Date: 2018-09-26 / tags: Python, ファイル操作

Pythonでファイルを削除するにはos.remove()、ディレクトリ(フォルダ)を中のファイルやサブディレクトリごとすべて削除するにはshutil.rmtree()を使う。空のディレクトリのみを削除対象とするos.rmdir(), os.removedirs()もある。

osモジュールもshutilモジュールも標準ライブラリに含まれているので追加のインストールは不要(importは必要)。

以下の内容について説明する。

  • ファイルを削除: os.remove()
  • ディレクトリを中身ごと削除: shutil.rmtree()
  • ディレクトリを空にする
  • 空のディレクトリを削除: os.rmdir(), os.removedirs()
  • 複数のファイルを一括で削除
    • ワイルドカードで条件指定
    • 正規表現で条件指定

いずれの場合もゴミ箱に入れられることなく削除されるので注意。削除ではなくshutil.move()で別のディレクトリに移動するという方法もある。以下の記事を参照。

また、Python3.4以降ではパスをオブジェクトとして操作できるpathlibモジュールを使ってファイルやディレクトリを削除することも可能。条件を満たすファイルの一覧を取得して削除するような処理はpathlibを使ったほうが書きやすい。

スポンサーリンク

ファイルを削除: os.remove()

ファイルを削除するのはos.remove()

例として以下のようにディレクトリ、ファイルを作成。

import os

os.makedirs('temp/', exist_ok=True)

with open('temp/file.txt', 'w') as f:
    f.write('')

print(os.listdir('temp'))
# ['file.txt']
source: os_remove.py

引数にファイルのパスを指定すると削除される。

os.remove('temp/file.txt')

print(os.listdir('temp'))
# []
source: os_remove.py

os.remove()で削除できるのはファイルのみ。ディレクトリ(フォルダ)のパスを指定するとエラーになる。

# os.remove('temp/')
# PermissionError: [Errno 1] Operation not permitted: 'temp/'
source: os_remove.py

ディレクトリを削除したい場合は以下で説明するshutil.rmtree()os.rmdir(), os.removedirs()などを使う。

ディレクトリを中身ごと削除: shutil.rmtree()

ディレクトリを中のファイルやサブディレクトリごと削除するにはshutil.rmtree()を使う。

空のディレクトリのみを削除したい場合はos.rmdir(), os.removedirs()(後述)。

例として以下のようにディレクトリ、ファイルを作成。

import shutil
import os

os.makedirs('temp/dir/sub_dir/', exist_ok=True)

with open('temp/dir/file.txt', 'w') as f:
    f.write('')

print(os.listdir('temp/'))
# ['dir']

print(os.listdir('temp/dir/'))
# ['file.txt', 'sub_dir']

shutil.rmtree()の引数として指定できるのはディレクトリを示すパスのみ。ファイルだとエラー。

# shutil.rmtree('temp/dir/file.txt')
# NotADirectoryError: [Errno 20] Not a directory: 'temp/dir/file.txt'

ディレクトリのパスを指定すると中のファイルやサブディレクトリごとまとめて削除される。

shutil.rmtree('temp/dir/')

print(os.listdir('temp/'))
# []

サブディレクトリ内にさらにファイルやディレクトリが存在する場合も再帰的にすべて削除される。

ディレクトリを空にする

ディレクトリを空にする場合は shutil.rmtree()で全部削除してから新しく同名のディレクトリをos.mkdir()で作成するのが簡単。

import os
import shutil

os.makedirs('temp/dir/', exist_ok=True)

with open('file.txt', 'w') as f:
    f.write('')

print(os.listdir('temp/'))
# ['dir', 'test.txt']

target_dir = 'temp'

shutil.rmtree(target_dir)
os.mkdir(target_dir)

print(os.listdir('temp/'))
# []
source: empty_dir.py

ディレクトリ内の複数のファイルを条件に応じて一括で削除する方法は後述。

空のディレクトリを削除: os.rmdir(), os.removedirs()

空のディレクトリのみを削除対象とするos.rmdir(), os.removedirs()もある。

os.rmdir()は空のディレクトリ一つが対象。

import os

os.makedirs('temp/dir_empty/', exist_ok=True)

print(os.listdir('temp/'))
# ['dir_empty']

os.rmdir('temp/dir_empty/')

print(os.listdir('temp/'))
# []
source: os_rmdir.py

ディレクトリが空でない(ファイルやサブディレクトリを含んでいる)場合はエラーとなる。

os.makedirs('temp/dir_not_empty/', exist_ok=True)

with open('temp/dir_not_empty/file.txt', 'w') as f:
    f.write('')

# os.rmdir('temp/dir_not_empty/')
# OSError: [Errno 66] Directory not empty: 'temp/dir_not_empty/'
source: os_rmdir.py

例外処理を行えばエラー(例外)で終了させずにディレクトリが空のときのみ削除することが可能。以下の例ではディレクトリが空ではない場合はpassで何の処理も行わずにスルーさせている。

try:
    os.rmdir('temp/dir_not_empty/')
except OSError as e:
    pass
source: os_rmdir.py

中身をまとめて削除する場合は上述のshutil.rmtree()を使う。

import shutil

shutil.rmtree('temp/dir_not_empty/')

print(os.listdir('temp/'))
# []
source: os_rmdir.py

os.removedirs()は空のディレクトリを末端から再帰的に削除する。引数に末端の空ディレクトリを指定すると、親ディレクトリが空である場合に順番に削除される。

例えば、 os.removedirs('foo/bar/baz') では最初にディレクトリ 'foo/bar/baz' を削除し、次に 'foo/bar' さらに 'foo' をそれらが空ならば削除します。末端のディレクトリが削除できなかった場合には OSError が送出されます。
os.removedirs() --- 雑多なオペレーティングシステムインタフェース — Python 3.7.0 ドキュメント

import os

os.makedirs('temp/dir1/dir2/dir3/', exist_ok=True)

with open('temp/file.txt', 'w') as f:
    f.write('')

print(os.listdir('temp/'))
# ['file.txt', 'dir1']

os.removedirs('temp/dir1/dir2/dir3/')

print(os.listdir('temp/'))
# ['file.txt']

複数のファイルを一括で削除

os.remove()を使った実践的な例として、条件に応じて複数のファイルを一括で削除する方法を説明する。

ワイルドカードで条件指定

globモジュールを使うとワイルドカード*などの特殊文字(メタ文字)を使ってファイルやディレクトリの一覧をリストで取得できる。

以下のようなファイル・ディレクトリ構成を例とする。

temp/
├── 012.txt
├── abc.txt
├── dir
│   ├── 789.txt
│   └── xyz.text
└── file.text

glob.glob()の第一引数にワイルドカードなどを含む文字列を指定する。

import glob

print(glob.glob('temp/*.txt'))
# ['temp/abc.txt', 'temp/012.txt']

Python3.5からは**と引数recursive=Trueを合わせて再帰的な処理が可能。

print(glob.glob('temp/**', recursive=True))
# ['temp/', 'temp/file.text', 'temp/abc.txt', 'temp/012.txt', 'temp/dir', 'temp/dir/xyz.text', 'temp/dir/789.txt']

サブディレクトリ内も含めて任意の拡張子のファイルのリストを取得するには以下のようにする。

print(glob.glob('temp/**/*.txt', recursive=True))
# ['temp/abc.txt', 'temp/012.txt', 'temp/dir/789.txt']

print(glob.glob('temp/**/*.text', recursive=True))
# ['temp/file.text', 'temp/dir/xyz.text']

?は任意の一文字、[]はカッコ内の一文字にマッチする。

print(glob.glob('temp/**/???.text', recursive=True))
# ['temp/dir/xyz.text']

print(glob.glob('temp/**/[0-9][0-9][0-9].txt', recursive=True))
# ['temp/012.txt', 'temp/dir/789.txt']

正規表現ではないので+で1回以上の繰り返しを指定したりはできない。正規表現を使った例は後述。

以下のような関数を定義するとglob.glob()で抽出された複数のファイルを一括で削除できる。

import os
import glob

def remove_glob(pathname, recursive=True):
    for p in glob.glob(pathname, recursive=recursive):
        if os.path.isfile(p):
            os.remove(p)

ディレクトリのパスが抽出されることもあるのでos.path.isfile()でファイルのみを対象としている。

サブディレクトリ内も含めて、拡張子txtのファイルを削除する。

remove_glob('temp/**/*.txt')

削除後のファイルは以下の通り。

temp/
├── dir
│   └── xyz.text
└── file.text

ディレクトリをマッチさせて削除したい場合はos.path.isfile()os.remove()のかわりにos.path.isdir()shutil.rmtree()を使えばOK。

正規表現で条件指定

正規表現で条件を指定したい場合はreモジュールを使う。

上の例と同じく以下のようなファイル・ディレクトリ構成を例とする。

temp/
├── 012.txt
├── abc.txt
├── dir
│   ├── 789.txt
│   └── xyz.text
└── file.text

glob.glob()**と引数recursive=Trueを使ってすべてのファイル、ディレクトリを再帰的にリストアップしてからre.search()などで正規表現を使った判定を行う。

リストを取得したい場合は以下のようにリスト内包表記が使える。

import glob
import re

print(glob.glob('temp/**', recursive=True))
# ['temp/', 'temp/file.text', 'temp/abc.txt', 'temp/012.txt', 'temp/dir', 'temp/dir/xyz.text', 'temp/dir/789.txt']

print([p for p in glob.glob('temp/**', recursive=True) if re.search('\d+\.txt', p)])
# ['temp/012.txt', 'temp/dir/789.txt']

print([p for p in glob.glob('temp/**', recursive=True) if re.search('/\D{3}\.(txt|text)', p)])
# ['temp/abc.txt', 'temp/dir/xyz.text']

\dは数字、+は1回以上の繰り返し。\Dは数字以外の文字、{n}はn回の繰り返し。また、(a|b)はa, bのいずれかとなる。globだけでは実現できない複雑な条件が指定できる。

以下のような関数を定義すると、glob.glob()および正規表現で抽出された複数のファイルを一括で削除できる。

import os
import glob
import re

def remove_glob_re(pattern, pathname, recursive=True):
    for p in glob.glob(pathname, recursive=recursive):
        if os.path.isfile(p) and re.search(pattern, p):
            os.remove(p)

サブディレクトリ内も含めて、拡張子txtでファイル名が数字のみのファイルを削除する。

remove_glob_re('\d+.txt', 'temp/**')

削除後のファイルは以下の通り。

temp/
├── abc.txt
├── dir
│   └── xyz.text
└── file.text

なお、ファイル・ディレクトリ数が多い場合にglob.glob()**を使うと時間がかかる可能性があるので、他の特殊文字で条件を絞れる場合はそちらを使ったほうがいい。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事