ファイルやディレクトリの場所を示すにはパス(あるいはファイルパス)と呼ばれる文字列が使われます。os.pathモジュールは、このパスに関するユーティリティを集めたモジュールです。具体的にはパスの結合や分解、パスが指すファイルの種類の確認、ファイルの存在確認などの機能を提供しています。
os.pathモジュールはパスを文字列として扱いますが、パスをオブジェクトとして扱うpathlibモジュールがPython3.4で追加されました。pathlibモジュールは、os.pathモジュールが提供する機能をカバーしているだけでなく、osモジュールの機能の一部もカバーし、さらにファイル入出力機能も提供し、パスに関する機能を包括的に提供しています。
os.pathモジュールについては次の記事で詳しく解説していますので、興味があればご参照ください。
パスの概要
最初に一般的なパスの基礎知識とPythonプログラム上で使用するパスセパレータについて解説します。
パスとは?
パス(あるいはファイルパス)とは、ファイルの所在を示すための文字列です。ディレクトリ階層をパスセパレータ(パス区切り文字)で区切って並べたものです。
例えばUnix系OSでパスは次のような文字列になります。
/var/tmp/file.txt
Unix系OSはパスセパレータとしてスラッシュ(/)を使います。先頭のスラッシュ(/)はパスセパレータではなく、ルートディレクトリを表します。
Windowsの場合、パスは次のようになります。
C:\Users\gekko\Documents
Windowsのパスは、ドライブ文字(ここではC)とボリューム区切り文字(:)で始まり、パスセパレータにはバックスラッシュ(\)を使います。
パスは絶対パスと相対パスに分類されます。上述のようにルートから始まるパスは絶対パスです。相対パスは、現在のディレクトリを基準として相対的にファイルの位置を示すパスです。例えば、上記の例で現在のディレクトが/varであれば、/var/tmp/file.txtの相対パスは次のように表すことができます。
tmp/file.txt
以上、パスについての基本的な知識を踏まえた上でPythonでパスを扱う方法を解説します。
Pythonで使用するパスセパレータ
前述した通りパスに使用するパスセパレータは、Unix系OSとWindowsで異なりますが、Pythonで関数の引数に渡したりするパスのパスセパレータとしてスラッシュ(/)を使うことができます。これはWindowsでPythonプログラムを実行する場合でも機能します。
>>> import os
>>> os.path.exists('var/tmp/file.txt')
True
Windows環境ではバックスラッシュ(\)を使っても構いませんが、バックスラッシュはエスケープ文字なので文字列の中では「\」を2つ重ねるか、raw文字列を使う必要があります。
>>> os.path.exists('var\\tmp\\file.txt')
True
>>> os.path.exists(r'var\tmp\file.txt')
True
パスを構築する
部分的なパスの構成要素をパスセパレータで繋げてパスを構築する方法にはいくつかのやり方がありますので、ここではその方法を紹介します。
手動でパス構築する
最も原始的な方法はパスの構成要素をパスセパレータで区切って手動で繋げていく方法です。
>>> 'var' + '/' + 'tmp' + '/' + 'file.txt'
'var/tmp/file.txt'
>>> 'var' + '\\' + 'tmp' + '\\' + 'file.txt'
'var\\tmp\\file.txt'
os.sep属性にはOSデフォルトのパスセパレータ(Unix系OSではスラッシュ、WIndowsではバックスラッシュ)が保存されているので、これを使えば環境に応じて適切なパスセパレータを使うことができます。
次のコードをUnix系OSで実行すると次のようになります。
>>> 'var' + os.sep + 'tmp' + os.sep + 'file.txt'
'var/tmp/file.txt'
Windowsで実行すると次のようになります。
>>> 'var' + os.sep + 'tmp' + os.sep + 'file.txt'
'var\\tmp\\file.txt'
os.path.join()で結合する
パスの構成要素を繋げるときにos.path.join()を使うこともできます。この関数もOSデフォルトのパスセパレータを使って、引数に渡されたすべてパスの要素を結合します。
Unix系OSで実行すると次のようになります。
>>> os.path.join('var', 'tmp', 'file.txt')
'var/tmp/file.txt'
Windowsでは次のようになります。
>>> os.path.join('var', 'tmp', 'file.txt')
'var\\tmp\\file.txt'
join()の引数の最後に空文字列を指定すると、結合されたパスはパスセパレータで終わります。
>>> unix_path2 = os.path.join(dir1, dir2, fname, '')
>>> print(unix_path2)
var/tmp/file.txt/
os.pathモジュールの関数
ここからはos.pathモジュールが提供する関数について解説します。パスを扱うさまざまな操作が提供されています。
絶対パスの取得 — abspath()
現在のパスから正規化された絶対パスに変換したい場合はabspath()を使います。
>>> os.path.abspath('foo.txt')
'/home/taro/testdir/foo.txt'
これはnormpath()を次のように呼び出したときと等価です。
>>> os.path.normpath(os.path.join(os.getcwd(), 'foo.txt'))
'/home/taro/testdir/foo.txt'
パスの正規化 — normpath()
パスの正規化とは、OSの標準的なパス形式に変換することです。正規化すると余分なパスセパレータ(//など)や「.」や「..」などを解決します。Windowsではスラッシュをバックスラッシュに変換します。
パスを正規化する関数はnormpath()です。引数に正規化するパスを渡します。
>>> os.path.normpath('dir1//bar.txt')
'dir1/bar.txt'
>>> os.path.normpath('dir1/./bar.txt')
'dir1/bar.txt'
>>> os.path.normpath('dir1/../dir1/bar.txt')
'dir1/bar.txt'
ベース名を取得する — basename()
basename()は引数に渡されたパスのペース名(末尾の要素)を返します。これはsplit()が返す2番目の要素と同じです。
>>> os.path.basename('/home/taro/testdir/foo.txt')
'foo.txt'
>>> os.path.basename('taro/testdir')
'testdir'
>>> os.path.basename('foo.txt')
'foo.txt'
パスがスラッシュで終わっていれば空文字列になります。
>>> os.path.basename('/home/taro/testdir/')
''
>>> os.path.basename('/')
''
ディレクトリを取得する — dirname()
dirname()は、引数に渡されたパスのディレクトリ部分を返します。これはsplit()が返す1番目の要素と同じです。
>>> os.path.dirname('/home/taro/testdir/foo.txt')
'/home/taro/testdir'
>>> os.path.dirname('taro/testdir')
'taro'
引数のパスがスラッシュで終わっていれば次のようにディレクトリ部分が返されます。
>>> os.path.dirname('/home/taro/testdir/')
'/home/taro/testdir'
>>> os.path.dirname('/')
'/'
パスにスラッシュが含まれていなければ空文字列になります。
>>> os.path.dirname('foo.txt')
''
パスを分割する — split()
split()は引数に渡されたパスを (ディレクトリ部分, 末尾の要素) に分割します。
>>> os.path.split('/home/taro/testdir/foo.txt')
('/home/taro/testdir', 'foo.txt')
>>> os.path.split('taro/testdir/foo.txt')
('taro/testdir', 'foo.txt')
>>> os.path.split('taro/testdir')
('taro', 'testdir')
パスがスラッシュで終わっていれば末尾の要素は空文字列になります。
>>> os.path.split('/home/taro/testdir/')
('/home/taro/testdir', '')
>>> os.path.split('/')
('/', '')
パスにスラッシュが含まれていなければディレクトリ部分は空文字列になります。
>>> os.path.split('foo.txt')
('', 'foo.txt')
ファイルが存在するか確認する — exists()
exists()メソッドは、パスが示すファイル(やディレクトリ)が存在する場合はTrueを返します。そうでなければFalseを返します。パスがシンボリックリンクを指している場合、シンボリックリンクが指しているファイルまたはディレクトリが存在しているかどうかの結果を返します。
>>> os.path.exists('/home/taro/testdir/foo.txt')
True
>>> os.path.exists('nonexist.txt')
False
>>> os.path.exists('.')
True
ホームディレクトリを解決する — expanduser()
expanduser()は、パスの先頭の「~」または「~user」をユーザーのホームディレクトリのパスに置き換えたものを返します。 置き換えに失敗したり、パスが「~」で始まっていない場合は、引数のパスをそのまま返します。
>>> Path('~/testdir/nonexist.txt').expanduser()
PosixPath('/home/taro/testdir/nonexist.txt')
タイムスタンプを取得する — getatime()、getmtime()、getctime()
ファイルには次の3つのタイムスタンプがあります。
- 最終アクセス時刻(atime)
- 最終修正時刻(mtime)
- 最終状態変更時刻(ctime)
os.pathモジュールには、これらのタイプスタンプを取得する関数が提供されています。
これらのタイムスタンプの詳細については次の記事が参考になります。
最終アクセス時刻(atime)を取得するにはgetatime()を使います。これはエポック(UTCの1970年1月1日)からの経過秒数を浮動小数点数で返します。
>>> os.path.getatime('foo.txt')
1695193044.2986088
最終修正時刻(mtime)を取得するにはgetmtime()を使います。これはエポック(UTCの1970年1月1日)からの経過秒数を浮動小数点数で返します。
>>> os.path.getmtime('foo.txt')
1695193041.1775389
最終状態変更時刻(ctime)を取得するにはgetctime()を使います。これはエポック(UTCの1970年1月1日)からの経過秒数を浮動小数点数で返します。
>>> os.path.getctime('foo.txt')
1695193041.1775389
これらの関数はファイルが存在しない、あるいはアクセスできない場合は OSErrorを送出します。
ファイルの種類を確認する — isfile()、isdir()、islink()
パスが指すファイルの種類が「通常ファイル」、「ディレクトリ」、「シンボリックリンク」であるかを確認する関数も提供されています。
次のようなファイルを用意してこれらの関数の動作を確認してみましょう。
$ ls -l
合計 4
drwxr-xr-x. 2 taro taro 21 9月 21 10:24 dir1
-rw-r--r--. 1 taro taro 7 9月 20 15:57 foo.txt
lrwxrwxrwx. 1 taro taro 4 9月 7 08:44 linkdir -> dir1
lrwxrwxrwx. 1 taro taro 7 9月 7 08:45 linkfile -> foo.txt
isfile()はパスが指す「ファイルが存在し、かつ通常ファイル(または通常ファイルへのシンボリックリンク)」であればTrueを返します。それ以外の場合はFalseを返します。
>>> os.path.isfile('dir1')
False
>>> os.path.isfile('foo.txt')
True
>>> os.path.isfile('linkdir')
False
>>> os.path.isfile('linkfile')
True
isdir()はパスが指す「ディレクトリが存在し、かつディレクトリ(またはディレクトリへのシンボリックリンク)」であればTrueを返し、それ以外はFalseを返します。
>>> os.path.isdir('dir1')
True
>>> os.path.isdir('foo.txt')
False
>>> os.path.isdir('linkdir')
True
>>> os.path.isdir('linkfile')
False
islink()は、パスが指す「ファイルが存在し、かつシンボリックリンク」であればTrueを返し、それ以外はFalseを返します。
>>> os.path.islink('dir1')
False
>>> os.path.islink('foo.txt')
False
>>> os.path.islink('linkdir')
True
>>> os.path.islink('linkfile')
True
絶対パスか確認する — isabs()
isabs()は引数に渡されたパスが絶対パスであればTrueを返し、そうでなければFalseを返します。
>>> os.path.isabs('/home/taro/testdir/foo.txt')
True
>>> os.path.isabs('testdir/foo.txt')
False
パスを結合する — join()
引数に渡された1つ以上のパスの構成要素をOSのパスセパレータで結合します。最後の引数が空文字列の場合、返されるパスの末尾はパスセパレータで終わります。パスセパレータはUnix系OSではスラッシュ(/)、Windowsではバックスラッシュ(\)です。
Unix系OSで実行するとスラッシュ(/)で結合します。
>>> os.path.join('home', 'taro', 'testdir')
'home/taro/testdir'
>>> os.path.join('home', 'taro', 'testdir', '')
'home/taro/testdir/'
Windowsで実行するとバックスラッシュ(\)で結合します。
>>> os.path.join('home', 'taro', 'testdir')
'home\\taro\\testdir'
>>> os.path.join('home', 'taro', 'testdir', '')
'home\\taro\\testdir\\'
おわりに
os.pathモジュールは簡単なパス文字列の操作などに適しています。しかし、今後はより高機能なpathlibモジュールを使った方が生産性が上がると思われます。
では、最後までお読みいただきありがとうございました。