ネストされたディレクトリを安全に作成するにはどうすればよいですか?

2008年11月08日に質問されました。  ·  閲覧回数 2.8M回  ·  ソース

Parand picture
2008年11月08日

ファイルが書き込まれるディレクトリが存在するかどうかを確認し、存在しない場合はPythonを使用してディレクトリを作成する最も洗練された方法は何ですか? これが私が試したものです:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

どういうわけか、私はos.path.existsを逃しました(kanja、Blair、およびDouglasに感謝します)。 これは私が今持っているものです:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

これを自動的に行う「オープン」のフラグはありますか?

回答

Blair Conrad picture
2008年11月08日
5553

Python≥3.5では、 pathlib.Path.mkdir使用します:

from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)

古いバージョンのPythonの場合、品質の良い2つの回答があり、それぞれに小さな欠陥があるので、それについて説明します。

os.path.exists試して、作成にos.makedirsを検討してください。

import os
if not os.path.exists(directory):
    os.makedirs(directory)

コメントや他の場所で指摘されているように、競合状態があります。ディレクトリがos.path.existsos.makedirs呼び出しの間に作成された場合、 os.makedirsOSError失敗します。 OSErrorを包括的にキャッチして続行することは、権限の不足やディスクのフルなどの他の要因によるディレクトリの作成の失敗を無視するため、絶対確実ではありません。

1つのオプションは、 OSErrorをトラップし、埋め込まれたエラーコードを調べることです( PythonのOSErrorから情報を取得するクロスプラットフォームの方法はあります

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

あるいは、2番目のos.path.existsが存在する可能性がありますが、別の人が最初のチェックの後にディレクトリを作成し、2番目のチェックの前にディレクトリを削除したとします。

アプリケーションによっては、同時操作の危険性は、ファイルのアクセス許可などの他の要因によってもたらされる危険性よりも多い場合と少ない場合があります。 開発者は、実装を選択する前に、開発中の特定のアプリケーションとその予想される環境について詳しく知る必要があります。

Pythonの最新バージョンは、 FileExistsError (3.3以降)を公開することで、このコードをかなり改善しています...

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

...そしてexist_okと呼ばれるos.makedirsへのキーワード引数を許可

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
Acumenus picture
2013年01月17日
1293

Python 3.5以降:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

上記で使用したpathlib.Path.mkdirは、ディレクトリを再帰的に作成し、ディレクトリがすでに存在する場合でも例外を発生させません。 親を作成する必要がない、または作成したくない場合は、 parents引数をスキップしてください。

Python 3.2以降:

pathlib

可能であれば、 pathlib2という名前の現在のpathlibバックポートをインストールします。 pathlibという名前の古いメンテナンスされていないバックポートをインストールしないでください。 次に、上記のPython 3.5以降のセクションを参照して、同じように使用します。

Python 3.4を使用している場合、 pathlibが付属していますが、便利なexist_okオプションがありません。 バックポートは、この欠落しているオプションを含むmkdirより新しく優れた実装を提供することを目的としています。

os

import os
os.makedirs(path, exist_ok=True)

上記で使用したos.makedirsは、ディレクトリを再帰的に作成し、ディレクトリがすでに存在する場合は例外を発生させません。 Python 3.2以降を使用している場合にのみ、オプションのexist_ok引数があり、デフォルト値はFalseです。 この引数は、2.7までのPython2.xには存在しません。 そのため、Python2.7のように手動で例外を処理する必要はありません。

Python 2.7以降:

pathlib

可能であれば、 pathlib2という名前の現在のpathlibバックポートをインストールします。 pathlibという名前の古いメンテナンスされていないバックポートをインストールしないでください。 次に、上記のPython 3.5以降のセクションを参照して、同じように使用します。

os

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

単純なソリューションでは、最初にos.path.isdir次にos.makedirsを使用する場合がありますが、上記のソリューションでは、2つの操作の順序が逆になります。 そうすることで、ディレクトリを作成する際の重複した試行に関係する一般的な競合状態を防ぎ、ディレクトリからファイルを明確にします。

OSError: [Errno 17] File exists 、つまりerrno.EEXISTはファイルとディレクトリの両方で発生するため、例外をキャプチャしてerrnoを使用することの有用性は限られていることに注意してください。 ディレクトリが存在するかどうかを確認するだけの方が信頼性が高くなります。

代替:

mkpathはネストされたディレクトリを作成し、ディレクトリがすでに存在する場合は何もしません。 これはPython2と3の両方で機能します。

import distutils.dir_util
distutils.dir_util.mkpath(path)

バグ10948によると、この代替手段の重大な制限は、特定のパスのPythonプロセスごとに1回だけ機能することです。 つまり、これを使用してディレクトリを作成し、Pythonの内部または外部からディレクトリを削除してから、もう一度mkpathを使用して同じディレクトリを再作成すると、 mkpathは単に無効なディレクトリをサイレントに使用します。以前にディレクトリを作成したことのキャッシュされた情報であり、実際にはディレクトリを再度作成することはありません。 対照的に、 os.makedirsはそのようなキャッシュに依存しません。 この制限は、一部のアプリケーションでは問題ない場合があります。


ディレクトリのモードについては、気になる場合はドキュメントを参照してください。

Heikki Toivonen picture
2011年02月18日
614

tryexceptを使用すると、errnoモジュールからの正しいエラーコードが競合状態を取り除き、クロスプラットフォームになります。

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

つまり、ディレクトリを作成しようとしますが、ディレクトリがすでに存在する場合は、エラーを無視します。 一方、その他のエラーは報告されます。 たとえば、事前にdir'a 'を作成し、そこからすべてのアクセス許可を削除すると、 errno.EACCES OSError発生します(アクセス許可が拒否されました、エラー13)。

crimsonstone picture
2009年01月15日
109

個人的には、 os.path.exists()代わりにos.path.isdir()を使用してテストすることをお勧めします。

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

あなたが持っている場合:

>>> dir = raw_input(":: ")

そして愚かなユーザー入力:

:: /tmp/dirname/filename.etc

... os.path.exists()テストした場合、その引数をos.makedirs()渡すと、 filename.etcという名前のディレクトリになります。

Douglas Mayle picture
2008年11月08日
84

os.makedirs確認してください:(完全なパスが存在することを確認します。)
ディレクトリが存在する可能性があるという事実を処理するには、 OSErrorキャッチします。 ( exist_okFalse (デフォルト)の場合、ターゲットディレクトリがすでに存在する場合はOSErrorが発生します。)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
hiro protagonist picture
2016年12月15日
79

Python 3.5以降、 pathlib.Path.mkdirにはexist_okフラグがあります。

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

これにより、ディレクトリが再帰的に作成され、ディレクトリがすでに存在する場合でも例外は発生しません。

os.makedirsがPython 3.2以降のexist_okフラグを取得したのと同じように(例: os.makedirs(path, exist_ok=True)


注:私がこの回答を投稿したとき、他の回答のどれもexist_ok言及していません...

Aaron Hall picture
2015年01月23日
48

この状況の詳細に関する洞察

特定のパスに特定のファイルを指定し、ファイルパスからディレクトリをプルします。 次に、ディレクトリがあることを確認した後、読み取り用にファイルを開こうとします。 このコードにコメントするには:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

組み込み関数dir上書きしないようにします。 また、 filepathまたはおそらくfullfilepathは、おそらくfilenameよりも優れたセマンティック名であるため、次のように記述したほうがよいでしょう。

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

最終目標は、最初に記述したこのファイルを書き込み用に開くことですが、基本的には次のように(コードに基づいて)この目標に近づいており、読み取り用にファイルを開きます。

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

読書のための開口部を想定

そこにあり、読み取ることができると期待するファイルのディレクトリを作成するのはなぜですか?

ファイルを開こうとするだけです。

with open(filepath) as my_file:
    do_stuff(my_file)

ディレクトリまたはファイルがない場合は、エラー番号が関連付けられたIOErrorが表示されます。 errno.ENOENTは、プラットフォームに関係なく、正しいエラー番号を指します。 必要に応じて、次のようにキャッチできます。

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

私たちが書くために開いていると仮定して

これはおそらくあなたが望んでいるものです。

この場合、競合状態に直面していない可能性があります。 ですから、今までどおりに行ってください。ただし、書き込みを行うには、 wモード(または追加するにはa )で開く必要があることに注意してください。 また、ファイルを開くためにコンテキストマネージャーを使用することもPythonのベストプラクティスです。

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

ただし、すべてのデータを同じディレクトリに配置しようとするPythonプロセスがいくつかあるとします。 次に、ディレクトリの作成について競合が発生する可能性があります。 その場合、 makedirs呼び出しをtry-exceptブロックでラップするのが最善です。

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)
gone picture
2008年11月08日
36

os.path.exists関数を試してください

if not os.path.exists(dir):
    os.mkdir(dir)
Ali Afshar picture
2008年11月08日
33

私は以下を書き留めました。 しかし、それは完全に絶対確実というわけではありません。

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

私が言っているように、これは本当に絶対確実ではありません。ディレクトリの作成に失敗する可能性があり、その期間中に別のプロセスがディレクトリを作成する可能性があるからです。

Aaron Hall picture
2015年01月23日
24

ディレクトリが存在するかどうかを確認し、必要に応じて作成しますか?

これに対する直接の答えは、他のユーザーやプロセスが自分のディレクトリをいじることを期待しない単純な状況を想定することです。

if not os.path.exists(d):
    os.makedirs(d)

または、ディレクトリの作成が競合状態の影響を受ける場合(つまり、パスが存在することを確認した後、他の何かがすでに作成している可能性がある場合)、次のようにします。

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

しかし、おそらくさらに良いアプローチは、 tempfile介して一時ディレクトリを使用することにより、リソース競合の問題を回避することです。

import tempfile

d = tempfile.mkdtemp()

オンラインドキュメントの要点は次のとおりです。

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

Python 3.5の新機能: pathlib.Pathexist_ok

パスで使用したいメソッドがたくさんある新しいPathオブジェクト(3.4以降)があります。そのうちの1つはmkdirです。

(コンテキストとして、スクリプトを使用して毎週の担当者を追跡しています。これは、同じデータに対して1日に2回以上Stack Overflowにヒットしないようにするスクリプトのコードの関連部分です。)

まず、関連するインポート:

from pathlib import Path
import tempfile

今すぐos.path.joinを処理する必要はありません-パス部分を/結合するだけです:

directory = Path(tempfile.gettempdir()) / 'sodata'

次に、ディレクトリが存在することを完全に確認します- exist_ok引数はPython3.5に表示されます。

directory.mkdir(exist_ok=True)

ドキュメントの関連部分は次のとおりです。

exist_okがtrueの場合、 FileExistsError例外は無視されます( POSIX mkdir -pコマンドと同じ動作)が、最後のパスコンポーネントが既存の非ディレクトリファイルでない場合に限ります。

これがもう少しスクリプトです-私の場合、競合状態の影響を受けず、ディレクトリ(または含まれているファイル)がそこにあることを期待するプロセスが1つだけあり、削除しようとするものは何もありませんディレクトリ。

todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
    logger.info("todays_file exists: " + str(todays_file))
    df = pd.read_json(str(todays_file))

Path strパスを期待する他のAPIがそれらを使用できるようにするには、 Pathオブジェクトをstrに強制変換する必要があります。

おそらく、Pandasは、抽象基本クラスos.PathLikeインスタンスを受け入れるように更新する必要があります。