Функциональность mkdir -p в Python

Есть ли способ получить функциональность, похожую на mkdir -p на оболочку из Python. Я ищу решение, отличное от системного. Я уверен, что код меньше 20 строк, и мне интересно, кто-то уже написал его?

Ответ 1

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

Update

Для Python ≥ 3.2, os.makedirs имеет необязательный третий аргумент exist_ok, который, когда true, включает функциональность 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.

Ответ 2

В Python >= 3.2 это

os.makedirs(path, exist_ok=True)

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

Ответ 3

Это проще, чем захват исключения:

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

Отказ от ответственности Этот подход требует двух системных вызовов, которые более подвержены условиям гонки в определенных условиях/условиях. Если вы пишете что-то более сложное, чем простая утилита script, работающая в контролируемой среде, вам лучше идти с принятым ответом, для которого требуется только один системный вызов.

ОБНОВЛЕНИЕ 2012-07-27

У меня возникает соблазн удалить этот ответ, но я думаю, что здесь есть значение в потоке комментариев. Таким образом, я конвертирую его в wiki.

Ответ 4

В последнее время я нашел это distutils.dir_util.mkpath:

In [17]: from distutils.dir_util import mkpath

In [18]: mkpath('./foo/bar')
Out[18]: ['foo', 'foo/bar']

Ответ 5

mkdir -p выдает сообщение об ошибке, если файл уже существует:

$ touch /tmp/foo
$ mkdir -p /tmp/foo
mkdir: cannot create directory `/tmp/foo': File exists

Таким образом, уточнение предыдущих предложений состояло бы в том, чтобы исключить исключение, если os.path.isdir возвращает False (при проверке на errno.EEXIST).

(Update) См. также этот очень похожий вопрос; Я согласен с принятым ответом (и оговорками), за исключением того, что я рекомендовал бы os.path.isdir вместо os.path.exists.

(Обновление). По предложению в комментариях полная функция будет выглядеть так:

import os
def mkdirp(directory):
    if not os.path.isdir(directory):
        os.makedirs(directory) 

Ответ 6

Как упоминалось в других решениях, мы хотим иметь возможность ударить файловую систему один раз, при этом имитируя поведение 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 терпят неудачу, но теперь на его месте находится каталог. Это не имеет большого значения, потому что функция будет работать только без молчания, не создавая исключения, когда во время последней файловой системы вызывал каталог.

Ответ 7

С Pathlib из стандартной библиотеки python3:

Path(mypath).mkdir(parents=True, exist_ok=True)

Если родители верны, любые отсутствующие родители этого пути создаются как необходимости; они создаются с разрешениями по умолчанию, не принимая (имитируя команду POSIX mkdir -p). Если exist_ok является ложным (по умолчанию), FileExistsError возникает, если целевой каталог уже существует.

Если exists_ok истинно, исключения FileExistsError будут игнорироваться (то же самое поведение как команда POSIX mkdir -p), но только если последний путь компонент не является существующим файлом без каталога.

Изменено в версии 3.5: Добавлен параметр exist_ok.

Ответ 8

Я думаю, что ответ 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

Они оба обрабатывают случай, когда путь уже существует молча, но допускают появление других ошибок.

Ответ 9

Объявление функции;

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))

Ответ 10

import os
import tempfile

path = tempfile.mktemp(dir=path)
os.makedirs(path)
os.rmdir(path)

Ответ 11

У меня был успех со следующим лично, но моя функция должна, вероятно, называться чем-то вроде "обеспечить, чтобы этот каталог существовал":

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

Ответ 12

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