ユーニックス総合研究所

  • home
  • archives
  • python-glob

Pythonのglobの使い方。再帰的にファイルのパスを取得する便利ライブラリ

  • 作成日: 2021-01-24
  • 更新日: 2024-01-06
  • カテゴリ: Python

Pythonのglobの使い方

Pythonにはパスを収集するライブラリ「glob(グロブ)」があります。
このglobを使うとファイルシステム上から簡単にファイルのパスを収集することが出来ます。

パターンによるマッチを行うので直観的にパスを収集可能です。

この記事ではglobを賢く使う方法を解説します。

globのインポート

globglobモジュールに定義されています。
ですので使用する場合はglobをインポートします。

import glob  

# glob.glob(...) で使える  

# あるいは  
from glob import glob  

# glob(...)で使える  

globの構造

glob()の構造は以下のようになっています。

glob.glob(  
    pathname,  # パス  
    *,  
    root_dir=None,  # 検索のルートフォルダ(ルートディレクトリ)  
    dir_fd=None,  # ディレクトリ記述子への相対パス  
    recursive=False,  # 真の場合、パターン「**」が再帰的にマッチ  
    include_hidden=False  # 真の場合、パターン「**」は隠しフォルダにマッチ  
)  

バージョン3.5から**の再帰的なマッチがサポートされています。
またバージョン3.10からroot_dirdir_fdの引数のサポート。
バージョン3.11からinclude_hidden引数がサポートされました。

返り値はpathnameにマッチしたパス名のリストになります。

glob()pathnameにマッチするパス名のリストを返しますが、この返り値は空になることもあります。
pathname/mnt/d/src/folder/Makefileのような絶対パス、それから../../folder/*/*.pngのような相対パスも使えます。
シェルでよく使われるワイルドカード(*)も使うことが出来ます。

参照先がない、壊れているシンボリックリンクも結果のリストには含まれますので、参照する場合は参照先があるか注意する必要があります。
返り値の結果がソートされているかどうかはファイルシステムによります。
glob()の実行中に参照中のファイルやフォルダが削除されたり追加されたりした場合は、それらのパス名が返り値に含まれるかどうかは不定になっています。つまり含まれるかもしれないし、含まれないかもしれません。

glob()のpathnameに指定できる展開記法

glob()の第1引数に指定するパターンはシェル由来のワイルドカードを指定できます。

ワイルドカード 一致対象
 ? 任意の一文字
 * 任意の文字列
 [set] set の中のいずれかの文字
 [!set] set の中のいずれでもない文字

(参考: 1. bash の基礎

任意の1文字

?を使うと、任意の1文字にマッチさせることができます。
たとえば↓のパターンは

file.?  

↓のファイルにマッチします。

file.c     file.h    file.o  

任意の文字列

*を使うと任意の文字列にマッチさせることができます。
たとえば↓のパターンは

my-*.txt  

↓のファイルにマッチします。

my-cat.txt    my-dog.txt    my-bird.txt  

setの中のいずれかの文字

[set]を使うと[]で囲まれたいずれかの文字にマッチさせることができます。
たとえば↓のパターンは

file-[ab].txt  

↓のファイルにマッチします。

file-a.txt    file-b.txt  

setの中のいずれでもない文字

[!set]を使うと[!]の中のいずれでもない文字にマッチさせることができます。
たとえば↓のパターンは

file-[!ab].txt  

↓のファイルなどにマッチします。

file-x.txt    file-y.txt  

解説に使うディレクトリ構造

今回は↓のようなディレクトリとファイルを解説に使います。

$ tree animals/  
animals/  
├── cat  
│    ├── mikeneko.jpg  
│    └── mikeneko.png  
└── dog  
    ├── chiwawa.jpg  
    └── chiwawa.png  

2 directories, 4 files  

globでディレクトリを得る

glob()でディレクトリを取得するには↓のようにディレクトリ名を指定します。

import glob  

files = glob.glob('animals')  
print(files)  
# ['animals']  

しかしパスをベタ書きするのであれば、そのパスを使えばいいので↑のようなコードはあまりglobの利点が目立っていません。

globで複数のファイルを集める

glob()で複数のファイルを集める場合は↓のようにワイルドカードを使います。

import glob  

files = glob.glob('animals/*')  
print(files)  
# ['animals/cat', 'animals/dog']  

↑の出力結果を見ると、animals/直下のディレクトリが収集できているのがわかります。
再帰的には収集していない点に注意が必要です。
再帰的に収集したい場合は後述の「globで再帰的にすべてのファイルを集める」をご覧ください。

また、特定の拡張子のファイルを収集したい場合は↓のようにします。

import glob  

files = glob.glob('animals/cat/*.png')  
print(files)  
# ['animals/cat/mikeneko.png']  

root_dir: globでルートフォルダを指定して検索する

root_dirに検索のルートフォルダ(ルートディレクトリ)を指定すると、そのフォルダを起点にして検索できます。
下記の例ではroot_diranimalsにして*でフォルダ内のフォルダを取得しています。

import glob  

files = glob.glob('*', root_dir='animals')  
print(files)  
# ['cat', 'dog']  

dir_fd: globで開いたフォルダのディスクリプタを指定する

glob()dir_fdにはファイルディスクリプタを指定できます。
ディスクリプタとは低水準ファイルI/Oのファイルを表す整数のことを言います。
このディスクリプタはos.open()で取得できます。
os.O_RDONLYで読み込み専用のフラグを指定してフォルダを開きます。
フォルダ? ファイルじゃないの? と思われるかもしれませんがフォルダはファイルの一種です。

import glob  
import os  

# フォルダのディスクリプタを取得  
dir_fd = os.open('/tmp/animals', os.O_RDONLY)  

# ディスクリプタを指定してマッチさせる  
files = glob.glob('*', dir_fd=dir_fd)  
print(files)  
# ['cat', 'dog']  

# ディスクリプタを閉じる  
os.close(dir_fd)  

注意点としてはディスクリプタは開いたらos.close()で閉じる必要があるということです。
ファイルとトイレのドアは開いたら閉じときましょう。

recursive: globで再帰的にすべてのファイルを集める

パターンに**を指定し、recursiveTrueにするとglob()はパスを再帰的に収集します。

import glob  

files = glob.glob('**', recursive=True)  
print(files)  
# ['animals', 'animals/cat', 'animals/cat/mikeneko.jpg', 'animals/cat/mikeneko.png', 'animals/dog', 'animals/dog/chiwawa.jpg', 'animals/dog/chiwawa.png', 'sample.py']  

sample.pyは↑のスクリプトです。
↑の出力を見るとanimals/ディレクトリを再帰的に下って行ってファイルのパスを取得しているのがわかります。

include_hidden: globで隠しフォルダにマッチさせる

globinclude_hiddenTrueにすると隠しファイルも取得できるようになります。
Linuxでは隠しファイルはドット(.)で始まるファイル名のファイルです。
このinclude_hiddenはバージョン3.11から使えます。

$ # 隠しファイルの作成(LinuxのBashで)  
$ touch animals/.hidden  
import glob  

files = glob.glob('animals/*', include_hidden=True)  
print(files)  
# ['animals/.hidden', 'animals/cat', 'animals/dog']  

globの実装

Pythonではglobglob.pyに実装されています。
glob()内部ではiglob()の結果をlist()に変換して返しています。
つまりglob()の動作のほとんどはiglob()によるものです。
さらにiglob()の内部では_iglob()_glob1_glob2なども呼ばれています。

cpython/Lib/glob.py at main · python/cpython · GitHub

おわりに

今回はPythonのライブラリglobの使い方を解説しました。
globはPythonのパワーを感じられる良いライブラリだと思います。
特定のディレクトリ以下のパスをまとめて取得したい時に威力を発揮するライブラリです。

関連記事
Djangoでオブジェクトを一括作成・更新【bulk_create, bulk_update】
DjangoのModel.objects.filter()の使い方【QuerySet】
Djangoのmodelのcreate()の使い方【Python】
Django入門: ルートの設定 ~ 簡単な一行掲示板アプリを作る その4【Windows10】
NumPyのappend()の使い方: 配列の末尾に要素を追加
Numpyのarangeの使い方: 指定範囲の数列を生成する
Python3でYoutube Data APIを使ってキーワード検索する
PythonからC言語(my.puts)を呼び出して実行する