Pythonでファイルを処理するとき、ディレクトリのファイル一覧が必要なことがよくあります。この記事では次の方法でディレクトリの一覧を取得する方法を紹介します。
方法はいくつかあります。この記事では次の方法について紹介いたします。
- os.listdir()関数でファイルを一覧する
- os.scandir()でファイルを一覧する
- pathlibモジュールのiterdir()メソッドでファイルを一覧する
- ディレクトリツリーを再帰的に走査する
- グロブ形式のパターンにマッチするファイル一覧する
os.listdir()関数でファイルを一覧する
ディレクトリの中のファイル(エントリ)一覧を取得するもっとも単純な方法は、os.listdir()を使う方法です。os.listdir()は引数に渡されたパスのディレクトリに含まれるエントリの一覧をリストで返します。引数を省略するとカレントディレクトリの一覧を返します。
>>> import os
>>> os.listdir('/home/taro/testdir')
['linkdir', 'dir1', 'dir2', 'file1.txt', 'file2.txt', 'linkfile']
>>> os.listdir()
['linkdir', 'dir1', 'dir2', 'file1.txt', 'file2.txt', 'linkfile']
返されるリストの順番は不定です。リストには「.」や「..」は含まれません。
ファイルの種類で分類する
ファイルの一覧には通常ファイルやディレクトリなどが含まれます。それらファイルの種類によって処理を分岐したいことが多々あります。ここでは、ついでにファイルの種類によって処理を分岐する方法も説明します。
ファイルが通常ファイル、ディレクトリ、シンボリックリンクファイルのいずれであるかは次のように確認できます。
>>> import os
>>> for entry in os.listdir():
... if os.path.isfile(entry):
... print(f'File: {entry}')
... if os.path.isdir(entry):
... print(f'Dir: {entry}')
... if os.path.islink(entry):
... print(f'Link: {entry}')
...
Dir: linkdir
Link: linkdir
Dir: dir1
Dir: dir2
File: file1.txt
File: file2.txt
File: linkfile
Link: linkfile
os.path.isfile()は、引数のパスが存在する通常ファイルであればTrueを返します。isfile()はリンクを辿るので、ファイルへのシンボリックリンクの場合もTrueを返します。os.path.isdir()は、引数のパスが存在するディレクトリであればTrueを返します。isdir()はリンクを辿るので、ディレクトリへのシンボリックリンクの場合もTrueを返します。os.path.islink()は引数のパスがリンクファイルであればTrueを返します。
os.path.isfile()とos.path.isdir()については以下の記事で詳しく解説していますので、併せて参照ください。
os.scandir()でファイルを一覧する
ディレクトリの中のファイル(エントリ)を一覧するには、os.scandir()も使えます。os.scandir()の引数にディレクトリのパスを渡して呼び出すとイテレーターオブジェクトを返します。引数を省略するのはドット(.)を指定したのと同等です。
>>> type(os.scandir('/home/taro/testdir'))
<class 'posix.ScandirIterator'>
このイテレーターオブジェクトは、イテレートするごとにディレクトリ内の各エントリを表すos.DirEntry オブジェクトを戻します。イテレータが返す各エントリの順番は不定で、「.」や「..」は含まれないのはlistdir()と同様です。
os.scandir()を呼び出すときは、次のようにwith文で呼び出すか明示的にclose()を呼び出すことが推奨されます。
>>> with os.scandir() as iter:
... for entry in iter:
... if entry.is_file():
... print('--- File ---')
... print(entry.name)
... print(entry.path)
... if entry.is_dir():
... print('--- Directory ---')
... print(entry.name)
... print(entry.path)
... if entry.is_symlink():
... print('--- Link ---')
... print(entry.name)
... print(entry.path)
...
--- Directory ---
linkdir
./linkdir
--- Link ---
linkdir
./linkdir
--- Directory ---
dir1
./dir1
--- Directory ---
dir2
./dir2
--- File ---
file1.txt
./file1.txt
--- File ---
file2.txt
./file2.txt
--- File ---
linkfile
./linkfile
--- Link ---
linkfile
./linkfile
イテレータがイテレートするごとに返すos.DirEntry オブジェクトはディレクトリ内の各エントリを表していることは前述しました。このDirEntry オブジェクトが表す(ベース)ファイル名はname属性で参照できます。
path属性は、scandir()の引数に渡されたパスとname属性の値を繋げたものになります。つまり、os.path.join(引数パス, name属性)と同等になります。
DirEntry オブジェクトのis_file()、is_dir()、is_symlink()は、os.pathのisfile()、isdir()、islink()と同等です。is_file()はオブジェクトがファイルまたはファイルへのシンボリックであればTrue、is_dir()はディレクトリまたはディレクトリへのシンボリックリンクであればTrue、is_symlink()はシンボリックリンクであればTrueを返します。
pathlibモジュールのiterdir()メソッドでファイルを一覧する
pathlibモジュールで定義されている具象パスクラスのiterdir()メソッドを使ってもディレクトリ内のファイルを一覧することができます。
具象パスクラスのiterdir()メソッドはジェネレータオブジェクトを戻します。このジェネレータオブジェクトは、パスオブジェクトが指すディレクトリ内の各ファイルを表すパスオブジェクトを順に生成します。ジェネレータオブジェクトが生成するパスオブジェクトの順序は不定で、「.」や「..」が含まれないことはこれまでと同様です。
>>> from pathlib import Path
>>> p = Path('.')
>>> for entry in p.iterdir():
... if entry.is_file():
... print('--- File ---')
... str(entry)
... if entry.is_dir():
... print('--- Directory ---')
... str(entry)
... if entry.is_symlink():
... print('--- Link ---')
... str(entry)
...
--- Directory ---
'linkdir'
--- Link ---
'linkdir'
--- Directory ---
'dir1'
--- Directory ---
'dir2'
--- File ---
'file1.txt'
--- File ---
'file2.txt'
--- File ---
'linkfile'
--- Link ---
'linkfile'
パスオブジェクトのis_file()、is_dir()、is_symlink()は、DirEntry オブジェクトの同名のメソッドと同様です。pathlibモジュールの詳しい使い方は次の記事を参照ください。
ディレクトリツリーを再帰的に走査する
ここまではあるディレクトリ内のファイルを一覧する方法を見てきましたが、ディレクトリ、そしてそのサブディレクトリと再帰的に辿ってディレクトリツリー全体を一覧するos.walk()という関数もあります。
次のようなディレクトリツリーを準備して、os.walk()の動作を確認してみましょう。
$ tree /home/taro/testdir
/home/taro/testdir
├── dir1
│ ├── foo1.txt
│ └── foo2.txt
├── dir2
│ ├── bar1.txt
│ ├── bar2.txt
│ └── subdir
│ └── baz1.txt
└── linkdir -> dir1
それでは /home/taro/testdir をルートとするディレクトリツリーを一覧してみましょう。os.walk()の引数にはディレクトリツリーのルートとなるパスを渡します。
>>> import os
>>> for root, dirs, files in os.walk('/home/taro/testdir'):
... print(f'Root: {root}')
... print(f'Dirs: {dirs}')
... print(f'Files: {files}')
... print('----------------')
...
Root: /home/taro/testdir
Dirs: ['linkdir', 'dir1', 'dir2']
Files: []
----------------
Root: /home/taro/testdir/dir1
Dirs: []
Files: ['foo1.txt', 'foo2.txt']
----------------
Root: /home/taro/testdir/dir2
Dirs: ['subdir']
Files: ['bar1.txt', 'bar2.txt']
----------------
Root: /home/taro/testdir/dir2/subdir
Dirs: []
Files: ['baz1.txt']
----------------
os.walk()はジェネレータオブジェクトを返します。これは現在辿っているディレクトリの情報を含む(dirpath, dirnames, filenames)というタプルを順に返します。dirpathはディレクトリパス(文字列)、dirnamesはそのディレクトリの中のディレクトリ(リスト)、filenamesはそのディレクトリの中のファイル(リスト)です。
os.walk()の詳しい使い方は、次の記事を参照ください。
グロブ形式のパターンにマッチするファイル一覧する
シェルのグロブ(ファイルグロブともいう)と同様なパターンを使って、パターンにマッチするファイルを一覧することもできます。グロブを使う関数を3つ紹介しましょう。
- globモジュールのglob()関数かiglob()関数
- pathlibモジュールのglob()メソッド
glob()の引数には、パスを表すパターン文字列を渡します。するとマッチしたファイルをリストで返します。
glob.glob()を使って「file」で始まるファイルを一覧してみましょう。
>>> import glob
>>> glob.glob('file*')
['file2', 'file1']
glob.iglob()は、マッチするファイル名を順に生成するジェネレータオブジェクトを返します。
>>> for entry in glob.iglob('file*'):
... entry
...
'file1'
'file2'
pathlibモジュールで定義された具象パスクラスのglob()メソッドもジェネレータオブジェクトを返しますが、このジェネレータオブジェクトが順に生成するのはマッチしたファイルを表す具象パスオブジェクトです。
>>> from pathlib import Path
>>> p = Path('.')
>>> for entry in p.glob('file*'):
... entry
...
PosixPath('file1')
PosixPath('file2')
「file」の後に任意の1文字があるパターンは次のとおりです。
>>> glob.glob('file?')
['file2', 'file1']
「file」を含む一覧は次のように作成できます。
>>> glob.glob('*file*')
['file2', 'linkfile', 'file1']
グロブを使ってファイルを一覧する方法については、次の記事で詳しく解説していますので興味があればご参照ください。
まとめ
ファイルを一覧するやり方はいくつもあります。用途に合わせてわかりやすい方法を選べば良いでしょう。