Pythonでディレクトリを作成したり、削除したりするにはいくつかの方法があります。ここではそれらの方法について解説します。
なお、pathlibモジュールを使った方法も紹介しますが、そのモジュールの詳しい使い方は解説していません。pathlibモジュールについては次の記事で詳しく解説していますので併せて参照ください。
ディレクトリを作成する
osモジュールのmkdir()関数は引数に指定されたパスのディレクトリを作成します。
>>> import os
>>> os.mkdir('dir1')
>>> os.path.isdir('dir1')
True
pathlibモジュールの具象パスクラスのmkdir()メソッドを使ってディレクトリを作成することもできます。
>>> from pathlib import Path
>>> p = Path('dir2')
>>> p.mkdir()
>>> p.is_dir()
True
どちらの方法でも、既にディレクトリが存在する場合はFileExistsErrorを送出します。
>>> os.mkdir('dir1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileExistsError: [Errno 17] File exists: 'dir1'
>>> p.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: 'dir2'
パスが存在しない場合はFileExistsErrorを送出します。
>>> os.mkdir('dir1/subdir1/foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'dir1/subdir1/foo'
>>> p = Path('dir2/subdir2/bar')
>>> p.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileNotFoundError: [Errno 2] No such file or directory: 'dir2/subdir2/bar'
アクセス権(モード)を指定する
ディレクトリを作成するときに、mode引数でディレクトリのアクセス権(モード)を指定することもできます。
>>> import stat
>>> os.mkdir('dir3', mode=0o666)
>>> stat3 = os.stat('dir3')
>>> stat.filemode(stat3.st_mode)
'drw-r--r--'
>>> p = Path('dir4')
>>> p.mkdir(mode=0o666)
>>> stat4 = p.stat()
>>> stat.filemode(stat4.st_mode)
'drw-r--r--'
アクセス権はプロセスのumask値と組み合わせて決定されます。この例のumask値は「0022」なので、アクセス権は「0o644」になります。
mode引数のデフォルト値は「0o777」なので、引数を省略すると「0o755」になります。
>>> stat1 = os.stat('dir1')
>>> stat.filemode(stat1.st_mode)
'drwxr-xr-x'
親ディレクトリも作成する
パスが示すディレクトリの親ディレクトリ(中間ディレクトリ)も作成したい場合は、os.makedirs()を使います。
>>> os.mkdir('dir1/subdir1/foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'dir1/subdir1/foo'
>>> os.makedirs('dir1/subdir1/foo', mode=0o666)
>>> os.path.exists('dir1/subdir1/foo')
True
mkdir()でディレクトリを作成しようとすると、親ディレクトリがないためエラーになります。makedirs()では親ディレクトリも作成するので成功します。
ディレクトリの作成時にmode引数を指定できるのはmkdir()と同様です。ただし、makedirs()のmode引数は末尾のディレクトリを作成のためにだけ使われ、親ディレクトリ(中間ディレクトリ)の作成には使用されません。
>>> foo_stat = os.stat('dir1/subdir1/foo')
>>> stat.filemode(foo_stat.st_mode)
'drw-r--r--'
>>> subdir1_stat = os.stat('dir1/subdir1')
>>> stat.filemode(subdir1_stat.st_mode)
'drwxr-xr-x'
具象パスクラスのmkdir()メソッドは、引数に「parents=True」を指定すれば親ディレクトリも作成できます。
>>> p.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileNotFoundError: [Errno 2] No such file or directory: 'dir2/subdir2/bar'
>>> p.mkdir(mode=0o666, parents=True)
>>> p = Path('dir2/subdir2/bar')
mode引数は末尾のディレクトリを作成のためにだけ使われ、親ディレクトリ(中間ディレクトリ)の作成には使用されないのは同様です。
>>> p = Path('dir2/subdir2/bar')
>>> stat.filemode(p.stat().st_mode)
'drw-r--r--'
>>> sub_p = Path('dir2/subdir2')
>>> stat.filemode(sub_p.stat().st_mode)
'drwxr-xr-x'
ディレクトリを削除する
ディレクトリを削除するにはos.rmdir()関数を使います。
>>> os.path.isdir('dir1/subdir1/foo')
True
>>> os.rmdir('dir1/subdir1/foo')
>>> os.path.exists('dir1/subdir1/foo')
False
具象パスクラスのrmdir()メソッドを使ってディレクトリを削除することもできます。
>>> p.is_dir()
True
>>> p.rmdir()
>>> p.exists()
False
どちらの方法で削除する場合でも、ディレクトリは空である必要があります。空でない場合はOSErrorを送出します。
>>> os.rmdir('dir1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 39] Directory not empty: 'dir1'
>>> p = Path('dir2')
>>> p.rmdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1363, in rmdir
self._accessor.rmdir(self)
OSError: [Errno 39] Directory not empty: 'dir2'
ディレクトリを再起的に削除する
ディレクトリを再起的に削除することもできます。それには、os.makedirs()を使います。
>>> os.makedirs('hoge/fuga/piyo')
>>> os.path.isdir('hoge/fuga/piyo')
True
>>> os.removedirs('hoge/fuga/piyo')
>>> os.path.exists('hoge')
False
この例では最初の「hoge/fuga/piyo」のディレクトリを作成し、それをos.makedirs()を使い再起的に削除しています。
os.removedirs(‘hoge/fuga/piyo’)は最初に「hoge/fuga/piyo」を削除します(末尾のpiyoディレクトリが空でなければOSErrorが送出されます)。次にfugaディレクトリが空であればそれを削除します。最後にhogeディレクトリが空であればそれを削除します。末尾のディレクトリ(piyo)以外のディレクトリが空でなければそのディレクトリが削除されないだけで、エラーが送出されることもありません。
これらの動作を確認するためにもう一度「hoge/fuga/piyo」のディレクトリ階層を作成します。次の例ではfugaディレクトリが空ではないのでOSErrorを送出しています。
>>> os.makedirs('hoge/fuga/piyo')
>>> os.removedirs('hoge/fuga')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/os.py", line 243, in removedirs
rmdir(name)
OSError: [Errno 39] Directory not empty: 'hoge/fuga'
この状態で「hoge/baz」ディレクトリを作成し、os.removedirs(‘hoge/fuga/piyo’)を実行してみます。
>>> os.mkdir('hoge/baz')
>>> os.removedirs('hoge/fuga/piyo')
>>> os.exists('hoge/fuga')
>>> os.path.exists('hoge/fuga')
False
>>> os.path.exists('hoge')
True
piyoディレクトリは空なので削除されます。次にfugaディレクトリは空なので削除されます。最後にhogeディレクトリを削除しようとしますが、bazディレクトリがあり空ではないので削除されません。
ディレクトリツリーを削除する
os.makedirs()は空であるディレクトリしか削除できませんでした。shutil.rmtree()を使えば、空ではないディレクトリ(ディレクトリツリー全体)を削除することができます。rmtree()の引数にはディレクトリを示すパスを渡す必要があります。
次のようなファイルを準備して動作を確認してみます。
$ tree dir
dir
├── subdir1
│ ├── file1.txt
│ └── file2.txt
└── subdir2
shutil.rmtree()を実行するとdirディレクトリ(とその下の全てのファイル)が削除されます。
>>> import shutil
>>> shutil.rmtree('dir')
>>> os.path.exists('dir')
False
まとめ
os.pathモジュールの使い方は単純でわかりやすいと思います。pathlibモジュールについては、モジュールの使い方に慣れる必要がありますが、このモジュールだけで多くのことができますので非常に便利です。興味がある方は是非pathlibモジュールの記事も参照ください。