Функциональность 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.

В Python >=3.2 это

os.makedirs(path, exist_ok=True)

В более ранних версиях используйте ответ @tzot.

Это проще, чем отловить исключение:

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, но с исправленной ошибкой, когда часть дерева уже существует

Другие вопросы по тегам