Python, pathlibでファイル一覧を取得(glob, iterdir)

Pythonのpathlibモジュールを使ってディレクトリ(フォルダ)内のファイルやサブディレクトリの一覧を取得する方法について説明します。再帰的に処理したり、ワイルドカード文字*や正規表現による条件を指定して一覧を抽出したりできます。 Pathオブジェクトの一覧が取得できるのでそれぞれに対して各種メソッドを使った操作を行うことも簡単。 pathlibはPython3.4から追加されたモジュール。ファイルやディレクトリのパスをオブジェクトとして操作できます。標準ライブラリに含まれているので追加のインストールは不要(importは必要)。

pathlib --- オブジェクト指向のファイルシステムパス — Python 3.7.1rc1 ドキュメント

ここでは以下の内容について説明します。

ディレクトリ内のファイル・サブディレクトリ一覧を取得: iterdir() 条件を指定して再帰的に一覧取得: glob() globモジュールとpathlibモジュールでの**の意味の違い 正規表現で条件指定 Pathオブジェクト一覧に対する処理の例 すべて絶対パスに変換 すべて文字列に変換 ファイルまたはディレクトリのみ抽出 ファイル名(basename)のみを抽出 条件で抽出したファイルを削除pathlibの基礎的な使い方については

以下のようなファイル・ディレクトリ構成を例とします。 temp ├── 1.txt ├── 12.text ├── 123.txt ├── [x].txt ├── aaa.text └── dir ├── 987.text ├── bbb.txt ├── sub_dir1 │ ├── 98.txt │ └── ccc.text └── sub_dir2 └── ddd.text

ディレクトリ内のファイル・サブディレクトリ一覧を取得: iterdir()

ディレクトリ内のファイル・サブディレクトリ一覧を取得するにはPathオブジェクトのiterdir()メソッドを使います。

pathlib - iterdir() --- オブジェクト指向のファイルシステムパス — Python 3.7.1rc1 ドキュメント

pathlib.Path()でPathオブジェクトを生成します。

import pathlib
import glob
import re
import pprint

p_temp = pathlib.Path('temp')

print(p_temp)
# temp

print(type(p_temp))
# <class 'pathlib.PosixPath'>

Pathの基本については

iterdir()メソッドはos.listdir()に似ているが、名前の通りリストではなくイテレータを返します。for文で使うのであればイテレータのままで問題ないが、リストにしたい場合はlist()を使います。 各要素はPathオブジェクト。 ここでは結果を見やすくするためにpprintを使っています。

print(type(p_temp.iterdir()))
# <class 'generator'>

pprint.pprint(list(p_temp.iterdir()))
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/aaa.text'),
#  PosixPath('temp/dir'),
#  PosixPath('temp/1.txt'),
#  PosixPath('temp/12.text'),
#  PosixPath('temp/123.txt')]

ファイルを指すPathオブジェクトからiterdir()を呼ぶとエラー。

# print(list(pathlib.Path('temp/1.txt').iterdir()))
# NotADirectoryError: [Errno 20] Not a directory: 'temp/1.txt'

iterdir()単体ではサブディレクトリ内のファイルやディレクトリを再帰的に取得することはできません。再帰的に一覧を取得したい場合は次に説明するglob()を使います。

条件を指定して再帰的に一覧取得: glob()

条件を指定して再帰的にファイル・サブディレクトリ一覧を取得するにはPathオブジェクトのglob()メソッドを使います。

pathlib - glob() --- オブジェクト指向のファイルシステムパス — Python 3.7.1rc1 ドキュメント

名前の通り、処理としてはglobモジュールと同じ。globモジュールの使い方などの詳細は

globモジュールのglob()はリストを返すが、Pathオブジェクトのglob()はイテレータを返します。for文で使うのであればイテレータのままで問題ないが、リストにしたい場合はlist()を使います。 以下はサブディレクトリ内を含むすべてのディレクトリの中から拡張子がtxtのファイルの一覧を取得する例を示します。 各要素はPathオブジェクト。

p_temp = pathlib.Path('temp')

print(type(p_temp.glob('**/*.txt')))
# <class 'generator'>

pprint.pprint(list(p_temp.glob('**/*.txt')))
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/1.txt'),
#  PosixPath('temp/123.txt'),
#  PosixPath('temp/dir/bbb.txt'),
#  PosixPath('temp/dir/sub_dir1/98.txt')]

glob()で使える特殊文字は以下の通り。

*: 長さ0文字以上の任意の文字列 ?: 任意の一文字 []: 特定の一文字

pprint.pprint(list(p_temp.glob('*')))
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/aaa.text'),
#  PosixPath('temp/dir'),
#  PosixPath('temp/1.txt'),
#  PosixPath('temp/12.text'),
#  PosixPath('temp/123.txt')]

pprint.pprint(list(p_temp.glob('dir/*/*.text')))
# [PosixPath('temp/dir/sub_dir1/ccc.text'),
#  PosixPath('temp/dir/sub_dir2/ddd.text')]

pprint.pprint(list(p_temp.glob('???.*')))
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/aaa.text'),
#  PosixPath('temp/123.txt')]

pprint.pprint(list(p_temp.glob('[a-z][a-z][a-z].*')))
# [PosixPath('temp/aaa.text')]

globモジュールとpathlibモジュールでの**の意味の違い

globモジュールとpathlibモジュールのglob()で**の意味が若干異なります。

globモジュールでの** 引数recursive=Trueの場合、あらゆるファイルや 0 個以上のディレクトリおよびサブディレクトリにマッチして再帰的な処理を行う pathlibモジュールのglob()での** 引数recursiveは存在せず、を使うと常に再帰的な処理を行う すべてのディレクトリおよびサブディレクトリを再帰的に走査する具体的な例を示す。 globモジュールでrecursive=Trueとしてを使うと、すべてのサブディレクトリを再帰的に操作し、ファイル・ディレクトリの一覧を返します。

pprint.pprint(glob.glob('temp/**', recursive=True))
# ['temp/',
#  'temp/[x].txt',
#  'temp/aaa.text',
#  'temp/dir',
#  'temp/dir/sub_dir1',
#  'temp/dir/sub_dir1/98.txt',
#  'temp/dir/sub_dir1/ccc.text',
#  'temp/dir/987.text',
#  'temp/dir/bbb.txt',
#  'temp/dir/sub_dir2',
#  'temp/dir/sub_dir2/ddd.text',
#  'temp/1.txt',
#  'temp/12.text',
#  'temp/123.txt']

pathlibモジュールのglob()での**の場合、はディレクトリにのみマッチします。ファイルにもマッチさせたい場合は/*とします。

pprint.pprint(list(p_temp.glob('**')))
# [PosixPath('temp'),
#  PosixPath('temp/dir'),
#  PosixPath('temp/dir/sub_dir1'),
#  PosixPath('temp/dir/sub_dir2')]

pprint.pprint(list(p_temp.glob('**/*')))
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/aaa.text'),
#  PosixPath('temp/dir'),
#  PosixPath('temp/1.txt'),
#  PosixPath('temp/12.text'),
#  PosixPath('temp/123.txt'),
#  PosixPath('temp/dir/sub_dir1'),
#  PosixPath('temp/dir/987.text'),
#  PosixPath('temp/dir/bbb.txt'),
#  PosixPath('temp/dir/sub_dir2'),
#  PosixPath('temp/dir/sub_dir1/98.txt'),
#  PosixPath('temp/dir/sub_dir1/ccc.text'),
#  PosixPath('temp/dir/sub_dir2/ddd.text')]

正規表現で条件指定

glob()で使えるのは限られた特殊文字のみだが、正規表現による条件指定と組み合わせることもできます。 リスト内包表記の条件分岐でre.serch()による判定を行う。Pathオブジェクトをstr()で文字列に変換する必要があるので気をつけてください。

例は以下の通り。

pprint.pprint([p for p in p_temp.glob('**/*')
               if re.search('\d+\.txt', str(p))])
# [PosixPath('temp/1.txt'),
#  PosixPath('temp/123.txt'),
#  PosixPath('temp/dir/sub_dir1/98.txt')]

pprint.pprint([p for p in p_temp.glob('**/*')
               if re.search('/\D{3}\.(txt|text)', str(p))])
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/aaa.text'),
#  PosixPath('temp/dir/bbb.txt'),
#  PosixPath('temp/dir/sub_dir1/ccc.text'),
#  PosixPath('temp/dir/sub_dir2/ddd.text')]

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

Pathオブジェクト一覧に対する処理の例

iterdir(), glob()いずれもPathオブジェクトの一覧が取得できるので、リスト内包表記を使ってそれらのオブジェクトに任意の処理を実行することができます。 いくつか具体例を示す。

すべて絶対パスに変換

絶対パスに変換するのはresolve()。

pprint.pprint([p.resolve() for p in p_temp.iterdir()])
# [PosixPath('/Users/mbp/Documents/my-project/python-snippets/notebook/temp/[x].txt'),
#  PosixPath('/Users/mbp/Documents/my-project/python-snippets/notebook/temp/aaa.text'),
#  PosixPath('/Users/mbp/Documents/my-project/python-snippets/notebook/temp/dir'),
#  PosixPath('/Users/mbp/Documents/my-project/python-snippets/notebook/temp/1.txt'),
#  PosixPath('/Users/mbp/Documents/my-project/python-snippets/notebook/temp/12.text'),
#  PosixPath('/Users/mbp/Documents/my-project/python-snippets/notebook/temp/123.txt')]

すべて文字列に変換

Pathオブジェクトはstr()関数で文字列に変換できます。

pprint.pprint([str(p) for p in p_temp.iterdir()])
# ['temp/[x].txt',
#  'temp/aaa.text',
#  'temp/dir',
#  'temp/1.txt',
#  'temp/12.text',
#  'temp/123.txt']

ファイルまたはディレクトリのみ抽出

is_file(), is_dir()でファイルまたはディレクトリかを判定できます。リスト内包表記の条件分岐でそれらのメソッドを使うとファイルまたはディレクトリのパスのみを抽出できます。

pprint.pprint([p for p in p_temp.iterdir() if p.is_file()])
# [PosixPath('temp/[x].txt'),
#  PosixPath('temp/aaa.text'),
#  PosixPath('temp/1.txt'),
#  PosixPath('temp/12.text'),
#  PosixPath('temp/123.txt')]

pprint.pprint([p for p in p_temp.iterdir() if p.is_dir()])
# [PosixPath('temp/dir')]

ファイル名(basename)のみを抽出

ファイル名(basename)はname属性で取得できます。ディレクトリのname属性はディレクトリ名そのものになります。

この例ではis_file()と組み合わせてディレクトリは除外してファイル名のみ抽出しています。

pprint.pprint([p.name for p in p_temp.iterdir() if p.is_file()])
# ['[x].txt', 'aaa.text', '1.txt', '12.text', '123.txt']

条件で抽出したファイルを削除

glob()の条件で抽出したファイルをunlink()で削除します。

pprint.pprint([p for p in p_temp.glob('**/*')
               if re.search('\d+\.txt', str(p))])
# [PosixPath('temp/1.txt'),
#  PosixPath('temp/123.txt'),
#  PosixPath('temp/dir/sub_dir1/98.txt')]

for p in p_temp.glob('**/*'):
    if re.search('\d+\.txt', str(p)) and p.is_file():
        p.unlink()

pprint.pprint([p for p in p_temp.glob('**/*')
               if re.search('\d+\.txt', str(p))])
# []

以下のようにリスト内包表記で一行で書くこともできます。 [p.unlink() for p in p_temp.glob('**/*') if re.search('\d+.txt', str(p)) and p.is_file()]

ディレクトリを削除したい場合はis_dir()で判定した上でshutli.rmtree()を使います。Pathオブジェクトのメソッドrmdir()は空のディレクトリのみが対象なので気をつけてください。

Last Updated: 6/26/2019, 10:34:03 PM