Функциональность mkdir -p в Python
Есть ли способ получить функциональность, аналогичную mkdir -p
на оболочке изнутри Python. Я ищу решение, отличное от системного вызова. Я уверен, что код меньше 20 строк, и мне интересно, если кто-то уже написал это?
12 ответов
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
имеет необязательный третий аргумент exist_ok
что, когда истина, позволяет mkdir -p
функциональность - если mode
предоставляется и существующий каталог имеет другие разрешения, чем намеченные; в таком случае, OSError
поднимается как ранее.
Обновление 2
Для 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(...)
Отказ от ответственности Этот подход требует двух системных вызовов, которые более восприимчивы к условиям гонки при определенных условиях / условиях. Если вы пишете что-то более сложное, чем простой одноразовый скрипт, работающий в контролируемой среде, вам лучше согласиться с принятым ответом, который требует только одного системного вызова.
ОБНОВЛЕНИЕ 2012-07-27
Я испытываю желание удалить этот ответ, но я думаю, что есть смысл в ветке комментариев ниже. Таким образом, я превращаю его в вики.
Недавно я нашел этот 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
Таким образом, уточнение предыдущих предложений было быraise
исключение, если os.path.isdir
возвращается False
(при проверке на errno.EEXIST
).
(Обновление) Смотрите также этот очень похожий вопрос; Я согласен с принятым ответом (и предостережениями), за исключением того, что я бы рекомендовал 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 вызывается, если целевой каталог уже существует.
Если для параметра origin_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: просить прощения, а не разрешения.
Поэтому первое, что мы должны сделать, это попытаться создать каталог, а затем, если он пойдет не так, выяснить, почему.
Как указывает Джейкоб Габриэльсон, один из случаев, которые мы должны рассмотреть, - это случай, когда файл уже существует, где мы пытаемся поместить каталог.
С mkdir -p
:
$ touch /tmp/foo
$ mkdir -p /tmp/foo
mkdir: cannot create directory '/tmp/foo': File exists
Аналогичное поведение в Python будет вызывать исключение.
Таким образом, мы должны решить, если это было так. К сожалению, мы не можем. Мы получаем то же сообщение об ошибке от makedirs, существует ли каталог (хорошо) или существует файл, препятствующий созданию каталога (плохо).
Единственный способ выяснить, что произошло, - это снова проверить файловую систему, чтобы увидеть, есть ли там каталог. Если есть, то вернитесь молча, в противном случае вызовите исключение.
Единственная проблема заключается в том, что файловая система может быть в другом состоянии, чем когда вызывался makedirs. Например: файл существовал, вызывая сбой makedirs, но теперь каталог на своем месте. Это на самом деле не имеет большого значения, потому что функция будет тихо завершать работу, не вызывая исключения, когда во время последнего вызова файловой системы каталог существовал.
Я думаю, что ответ Асы по существу правильный, но вы могли бы немного расширить его, чтобы вести себя как 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))
import os
import tempfile
path = tempfile.mktemp(dir=path)
os.makedirs(path)
os.rmdir(path)
У меня был успех со следующим лично, но моя функция, вероятно, должна называться что-то вроде "убедиться, что этот каталог существует":
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
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, но с исправленной ошибкой, когда часть дерева уже существует