Pythonの内部からシェルのmkdir -p
のような機能を取得する方法はありますか。システムコール以外の解決策を探しています。私はコードが20行未満であると確信しています、そして誰かがすでにそれを書いたかどうか疑問に思いますか?
mkdir -p
機能は次のとおりです。
import errno
import os
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
Python≥3.2の場合、os.makedirs
には オプションの3番目の引数exist_ok
があり、trueの場合、mkdir -p
機能が有効になります—unlessmode
が提供され、既存のディレクトリには意図したものとは異なる権限があります。その場合、OSError
は以前と同様に発生します。
Python≥3.5には、 pathlib.Path.mkdir
もあります。
import pathlib
pathlib.Path("/tmp/path/to/desired/directory").mkdir(parents=True, exist_ok=True)
exist_ok
パラメーターはPython 3.5で追加されました。
これは例外をトラップするよりも簡単です。
import os
if not os.path.exists(...):
os.makedirs(...)
免責条項 このアプローチは2つのシステムコールを必要とします。制御された環境で実行される単純な使い捨てのスクリプトよりも洗練されたものを書いているのであれば、1つのシステムコールしか必要としないという受け入れられた答えを使ったほうがよいでしょう。
更新2012-07-27
私はこの答えを削除したいのですが、私は以下のコメントスレッドに価値があると思います。そのため、私はそれをWikiに変換しています。
最近、私はこれを見つけました distutils.dir_util.mkpath :
In [17]: from distutils.dir_util import mkpath
In [18]: mkpath('./foo/bar')
Out[18]: ['foo', 'foo/bar']
ファイルが既に存在する場合、mkdir -p
はエラーを返します。
$ touch /tmp/foo
$ mkdir -p /tmp/foo
mkdir: cannot create directory `/tmp/foo': File exists
そのため、os.path.isdir
がraise
を返す場合(errno.EEXIST
をチェックするとき)、以前の提案を改良して例外を-False
にすることになります。
(更新)こちらも参照してください 非常によく似た質問 ;私はos.path.isdir
の代わりにos.path.exists
を推薦する以外は、受け入れられた答え(および警告)に同意します。
(更新)コメントの提案によると、フル機能は次のようになります。
import os
def mkdirp(directory):
if not os.path.isdir(directory):
os.makedirs(directory)
Pathlib を使ってpython3標準ライブラリから:
Path(mypath).mkdir(parents=True, exist_ok=True)
両親が正しい場合は、このパスに足りない両親が必要に応じて作成されます。それらはモードを考慮に入れずにデフォルトのパーミッションで作成されます(POSIX mkdir -pコマンドを真似て)。 exist_okがfalse(デフォルト)の場合、ターゲットディレクトリがすでに存在する場合はFileExistsErrorが送出されます。
Exist_okがtrueの場合、FileExistsError例外は無視されます(POSIXのmkdir -pコマンドと同じ動作)。ただし、最後のパスコンポーネントが既存のディレクトリ以外のファイルでない場合のみです。
バージョン3.5で変更された仕様: exist_okパラメータが追加されました。
他の解決策で述べたように、私たちはmkdir -p
の振る舞いを真似ている間にファイルシステムを一度ヒットできるようにしたいです。私はこれが可能であるとは思わないが、我々はできるだけ近くなるべきである。
最初にコーディングし、後で説明します。
import os
import errno
def mkdir_p(path):
""" 'mkdir -p' in Python """
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
@ tzotの回答に対するコメントが示すように、実際にディレクトリを作成する前にディレクトリを作成できるかどうかを確認することには問題があります。その間に誰かがファイルシステムを変更したかどうかはわかりません。これは、許可ではなく、許しを求めるPythonのスタイルにも当てはまります。
だから私たちが最初にすべきことはディレクトリを作成しようとすることです、そしてそれがうまくいかなかったら、それを解決してください。
Jacob Gabrielsonが指摘しているように、私たちが探さなければならないケースの1つは、ディレクトリを配置しようとしている場所にファイルが既に存在するケースです。
mkdir -p
の場合:
$ touch /tmp/foo
$ mkdir -p /tmp/foo
mkdir: cannot create directory '/tmp/foo': File exists
Pythonの同様の動作は例外を発生させることです。
そうであるならば、我々は解決しなければなりません。残念ながら、できません。ディレクトリが存在する(good)か、ディレクトリの作成を妨げるファイルが存在する(bad)かにかかわらず、makedirsから同じエラーメッセージが返されます。
起こったことを解決する唯一の方法はディレクトリがそこにあるかどうか見るためにファイルシステムを再度点検することです。もしあれば、静かに戻って、そうでなければ例外を発生させます。
唯一の問題は、makedirsが呼び出されたときとはファイルシステムが異なる状態になっている可能性があることです。例:ファイルが存在してmakedirが失敗しましたが、今度はその場所にディレクトリがあります。最後のファイルシステム呼び出しの時点でディレクトリが存在していた場合、関数は例外を発生させずに静かに終了するだけなので、それはそれほど重要ではありません。
Asaの答えは本質的に正しいと思いますが、mkdir -p
のように振る舞うように少し拡張することもできます。
import os
def mkdir_path(path):
if not os.access(path, os.F_OK):
os.mkdirs(path)
または
import os
import errno
def mkdir_path(path):
try:
os.mkdirs(path)
except os.error, e:
if e.errno != errno.EEXIST:
raise
これらは両方とも、パスがすでに静かに存在しているが、他のエラーが発生することを許容している場合を扱います。
関数宣言
import os
def mkdir_p(filename):
try:
folder=os.path.dirname(filename)
if not os.path.exists(folder):
os.makedirs(folder)
return True
except:
return False
使用法 :
filename = "./download/80c16ee665c8/upload/backup/mysql/2014-12-22/adclient_sql_2014-12-22-13-38.sql.gz"
if (mkdir_p(filename):
print "Created dir :%s" % (os.path.dirname(filename))
私は個人的には次のようなことに成功しましたが、私の関数はおそらく「このディレクトリが存在することを確認する」のような名前で呼ばれるべきです。
def mkdirRecursive(dirpath):
import os
if os.path.isdir(dirpath): return
h,t = os.path.split(dirpath) # head/tail
if not os.path.isdir(h):
mkdirRecursive(h)
os.mkdir(join(h,t))
# end mkdirRecursive
import os
import tempfile
path = tempfile.mktemp(dir=path)
os.makedirs(path)
os.rmdir(path)
import os
from os.path import join as join_paths
def mk_dir_recursive(dir_path):
if os.path.isdir(dir_path):
return
h, t = os.path.split(dir_path) # head/tail
if not os.path.isdir(h):
mk_dir_recursive(h)
new_path = join_paths(h, t)
if not os.path.isdir(new_path):
os.mkdir(new_path)
@Dave Cの答えに基づいていますが、ツリーの一部がすでに存在する場所にバグが修正されています