Python で指定した拡張子や名前のファイルを列挙する (glob.iglob, glob.glob)

指定した拡張子を持つファイルを列挙する

ここでは、Python でカレントディレクトリ以下の .java ファイルを列挙する例を示します。

glob を使用する方法(オススメ)

glob.iglob 関数や glob.glob 関数を使用すると、いわゆるグロブ(シェルのワイルドカード)によるパターン指定でファイルを列挙することができます。 glob.iglob はジェネレーターで、glob.glob は通常の関数です。 つまり、glob.iglob は for ループでイテレート処理したいとき、glob.glob はリストで取得したいときに使います。

glob.iglob でファイル列挙
import glob

for name in glob.iglob("*.java"):
    print(name)

Python 3.5 以降では、ディレクトリを再帰的にたどるための ** も使用できます。 この場合は recursive パラメータを True に設定します。

glob.iglob で再帰的なファイル列挙
import glob

for name in glob.iglob("**/*.java", recursive=True):
    print(name)

実はオブジェクト指向的な方法でファイルパスを扱うための pathlib.Path クラス にも glob メソッドが用意されていて、同様の方法でファイルパスを列挙できます。 この glob メソッドは glob.glob 関数とは異なり、再帰的にディレクトリをたどるようにデフォルトで設定されており、ジェネレーターを返すようになっています。 ややこしいですね。

Path#glob メソッドで再帰的なファイル列挙
from pathlib import Path

for name in Path(".").glob("**/*.java"):
    print(name)

os.walk を使用する方法

glob を使用せずに、os.walk などを使用して自力でファイルを検索することもできます。 ここでは、os.walk ですべてのファイルを再帰的に列挙しつつ、fnmatch.fnmatch を使って指定した拡張子に一致するかを調べています。

os.walk で再帰的に .java ファイルを列挙
import fnmatch
import os

for dirpath, dirs, files in os.walk("."):
    for name in files:
        if fnmatch.fnmatch(name, "*.java"):
            print(os.path.join(dirpath, name))

実は、glob.iglob の実装もこのように os.walkfnmatch を組み合わせて実装されています。

指定した名前に完全に一致するファイルを検索する

ファイル名が完全に一致するファイルを検索したい場合も、glob.iglob を使って検索することができます。 ファイルを1つだけ見つけたい場合は、下記のようにループを break で抜けてしまえばよいでしょう。

カレントディレクトリ以下から指定した名前のファイルを検索
import glob

for name in glob.iglob("**/SampleApp.java", recursive=True):
    print(name)
    break

応用例: 指定した拡張子を持つファイルを grep する

下記の Python スクリプトは、カレントディレクトリ以下のすべての .java ファイルの内容を読み込み、import で始まる行を抽出して出力します。

extract_imports.py(.java ファイル内の import 行を抽出)
import glob

for filename in glob.iglob("**/*.java", recursive=True):
    with open(filename, encoding="utf-8") as f:
        for i, line in enumerate(f, start=1):
            if line.startswith("import "):
                print("{} [{}]: {}".format(filename, i, line.rstrip("\r\n")))
実行結果
$ python extract_imports.py
com/example/myapp/Main.java [1]: import java.util.concurrent.Callable;
com/example/myapp/Main.java [2]: import java.util.concurrent.ExecutionException;
com/example/myapp/Main.java [3]: import java.util.concurrent.FutureTask;
com/example/myapp/Db.java [1]: import java.sql.Connection;
com/example/myapp/Db.java [2]: import java.sql.PreparedStatement;
...