Как безопасно создать вложенный каталог?

Какой самый элегантный способ проверить, существует ли каталог, в который будет записан файл, и если нет, создать его с помощью 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)

Есть ли флаг для "open", который заставляет это происходить автоматически?

Комментарии к вопросу (6)
Решение

Я вижу два ответа с хорошими качествами, каждый из которых имеет небольшой недостаток, поэтому я выскажу свое мнение по этому поводу:

Попробуйте os.path.exists, и рассмотрите os.makedirs для создания.

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

Как отмечалось в комментариях и в других местах, существует – если каталог будет создан между вызовами os.path.exists и os.makedirs, то os.makedirs завершится с OSError. К сожалению, отлавливание OSError и продолжение работы не является надежным, так как при этом будет проигнорирован сбой создания каталога из-за других факторов, таких как недостаточные разрешения, полный диск и т.д.

Одним из вариантов может быть перехват OSError и изучение встроенного кода ошибки (см. Существует ли кроссплатформенный способ получения информации из OSError в Python):

import os, errno

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

Как вариант, может быть второй os.path.exists, но предположим, что другой создал каталог после первой проверки, а затем удалил его до второй – нас все равно могут обмануть.

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

Современные версии Python значительно улучшили этот код, как за счет раскрытия FileExistsError (в 3.3+)...

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

...и разрешением аргумента ключевого слова в os.makedirs под названием exist_ok (в 3.2+).

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
Комментарии (8)

Python 3.5+:

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

pathlib.Path.mkdir, как использовано выше, рекурсивно создает каталог и не вызывает исключения, если каталог уже существует. Если вам не нужно или вы не хотите, чтобы создавались родители, пропустите аргумент parents.

Python 3.2+:

Использование pathlib:.

Если есть возможность, установите текущий бэкпорт pathlib под названием pathlib2. Не устанавливайте старый необработанный бэкпорт под названием pathlib. Далее, обратитесь к разделу о Python 3.5+ выше и используйте его так же.

Если вы используете Python 3.4, хотя он и поставляется с pathlib, в нем отсутствует полезная опция exist_ok. Этот бэкпорт призван предложить более новую и совершенную реализацию mkdir, которая включает эту недостающую опцию.

Использование os:.

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

os.makedirs, как использовано выше, рекурсивно создает каталог и не вызывает исключения, если каталог уже существует. Опциональный аргумент exist_ok доступен только при использовании Python 3.2+, по умолчанию он имеет значение False. В Python 2.x до версии 2.7 этот аргумент отсутствует. Поэтому нет необходимости в ручной обработке исключений, как в Python 2.7.

Python 2.7+:

Использование pathlib:.

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

Обратите внимание, что перехват исключения и использование errno имеет ограниченную полезность, поскольку OSError: [Errno 17] File exists, т.е. errno.EEXIST, возникает как для файлов, так и для каталогов. Надежнее просто проверить, существует ли каталог.

Альтернатива:

mkpath создает вложенный каталог и ничего не делает, если каталог уже существует. Это работает как в Python 2, так и в Python 3.

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

Согласно Bug 10948, серьезным ограничением этой альтернативы является то, что она работает только один раз в каждом процессе python для данного пути. Другими словами, если вы используете его для создания каталога, затем удалите каталог из Python или извне, а затем снова используете mkpath для воссоздания того же каталога, mkpath будет просто молча использовать свою недействительную кэшированную информацию о предыдущем создании каталога, и не будет фактически создавать каталог снова. В отличие от этого, os.makedirs не полагается ни на какой такой кэш. Это ограничение может быть приемлемым для некоторых приложений.


Что касается режима каталога, пожалуйста, обратитесь к документации, если вас это волнует.

Комментарии (10)

Использование try except и правильного кода ошибки из модуля 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

Другими словами, мы пытаемся создать каталоги, но если они уже существуют, мы игнорируем ошибку. С другой стороны, о любой другой ошибке будет сообщено. Например, если вы предварительно создадите каталог 'a' и снимите с него все разрешения, вы получите OSError с errno.EACCES (Permission denied, error 13).

Комментарии (20)

Я бы лично рекомендую использовать ОС.путь.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

... Вы'вновь собирается в конечном итоге с папку именем.и т. д. Когда вы пройдете этот аргумент ОС.makedirs () если тест с ОС.путь.существует().

Комментарии (3)

Проверьте os.makedirs: (Проверяет, существует ли полный путь.) Для обработки факта, что каталог может существовать, перехватывается OSError. (Если exist_ok равно False (по умолчанию), то OSError будет вызван, если целевой каталог уже существует).

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
Комментарии (3)

Начиная с Python 3.5, pathlib.Путь.команды 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)

Это рекурсивно создает каталог и не вызывает исключение, если каталог уже существует.

(просто как ОС.makedirs получил exist_ok флаг начиная с Python 3.2 е.г'ОС.makedirs(путь, exist_ok=истина)`)

Комментарии (0)

Выводы о специфике этой ситуации

Вы даете конкретный файл в определенный путь, а вы тянете каталог из пути к файлу. Затем убедившись в том, что у вас есть каталог, при попытке открыть файл для чтения. Прокомментировать этот код:

имяфайла = " По - /мой/каталог/именем.тхт"и реж = ОС.путь.каталог(именем)

Мы хотим избежать перезаписи функции строение, реж. Кроме того, путь_к_файлу или возможно fullfilepath - пожалуй лучшее смысловое название, чем "имя файла", так что это будет лучше написано:

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

Вашей конечной целью является, чтобы открыть этот файл, вам первоначально состоянии, для написания, но вы'ре по сути приближаемся к этой цели (на основе кода) такой, который открывает файл для чтения:

если не ОС.путь.существует(каталог): ОС.makedirs(каталог) F = файл(имя файла)

При условии открытия для чтения

Зачем вы делаете каталог для файла, который вы ожидаете, чтобы быть там и быть в состоянии прочитать?

Просто попытка открыть файл.

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

Если каталог или файл, это'т здесь, вы'll получить для себя IOError с номерами ошибка: ошибка.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 режим (или"", чтобы добавить). Это's также питон рекомендуется использовать контекст-менеджера для открытия файлов.

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)
Комментарии (0)

Попробуйте использовать функцию os.path.exists.

if not os.path.exists(dir):
    os.mkdir(dir)
Комментарии (3)

Я поставил следующие вниз. Это's не полностью надежной, хотя.

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

Теперь, как я говорю, это не очень надежно, потому что мы имеем возможность не создавать каталог, а еще процесс ее создания в течение этого периода.

Комментарии (2)

проверить, существует ли каталог и создайте ее при необходимости?

Прямого ответа на это, предполагая, простой ситуации, когда вы не'т ожидать, что другие пользователи или процессы, чтобы быть возиться с вашего каталога:

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

Здесь'ы Essentials из интернет-дока:

mkdtemp(суффикс='', префикс='tпл', Дир=нет) вызываемые пользователем функции, чтобы создать и вернуть уникальные временные каталог. Возвращаемым значением является имя пути каталога.

каталог чтение, запись и просмотр только создание пользователей.

абонент отвечает за удаление каталога, когда покончим с этим.

Нового в Python 3.5: pathlib.Путь с exist_ok

Там's объекта новый "путь" (как 3.4) с большим количеством методов можно было бы хотеть использовать с путями - одна из которых команды mkdir.

(Для контекста, Я'м отслеживая мою еженедельную рэп со сценарием. Здесь'ы соответствующие части кода из скрипта, которые позволяют мне избежать удара переполнение стека более чем один раз в день на одни и те же данные.)

Первый соответствующий импорт:

from pathlib import Path
import tempfile

Мы не'т приходится иметь дело с ОС.путь.присоединяйтесь сейчас - просто соединить части пути с /:

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

Тогда я идемпотентным образом обеспечить существует каталог - аргумент exist_ok появляется в Python 3.5:

directory.mkdir(exist_ok=True)

Здесь's в соответствующей части документация:

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

Здесь's немного больше сценария - в моем случае, я'м не подлежит гонки, у меня только один процесс, который ожидает каталога (или содержащиеся в ней файлы), чтобы быть там, и я Дон'т иметь все, что пытается удалить каталог.

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

"Путь" объекты должны быть приведены к ул. перед другими API, которые ожидают ул. пути могут их использовать.

Возможно, панды должны быть обновлены, чтобы принять экземпляры абстрактный базовый класс, ОС.PathLike`.

Комментарии (0)

В Python 3.4 вы также можете использовать [модуль новый pathlib] (https://docs.python.org/3/library/pathlib.html):

from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
    if not path.parent.exists():
        path.parent.mkdir(parents=True)
except OSError:
    # handle error; you can also catch specific errors like
    # FileExistsError and so on.
Комментарии (2)

Соответствующим документации Python рекомендует использовать EAFP стиль кодирования (проще попросить прощения, чем разрешения). Это означает, что код

try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
    else:
        print "\nBE CAREFUL! Directory %s already exists." % path

это лучше, чем альтернатива

if not os.path.exists(path):
    os.makedirs(path)
else:
    print "\nBE CAREFUL! Directory %s already exists." % path

Документация предполагает, что это именно из-за состояния гонки, обсуждали этот вопрос. Кроме того, как говорят здесь, есть преимущество в производительности в один запрос вместо двух ОС. Наконец, аргумент смещен вперед, потенциально, в пользу второго кода в некоторых случаях, когда разработчик знает среду выполнения приложения-только может быть выступал в особом случае, что программа была создана частная условий для себя (и других экземпляров той же программе).

Даже в этом случае, это плохая практика и может привести к длительным бесполезным отладки. Например, тот факт, что мы установить разрешения для папки не должно покидать нас с разрешения впечатление заданы правильно для наших целей. Родительского каталога может быть установлен с другими разрешениями. В общем, программа всегда должна работать правильно, и программист не должен ждать определенной среде.

Комментарии (0)

В Питон3, ОС.makedirs` поддерживает параметрexist_ok. Значение по умолчаниюложь, которое означает ``OSError будет повышено, если целевой каталог уже существует. Установив exist_ok в Правда, OSError (каталог существует) будет игнорироваться, а каталог не будет создан.

os.makedirs(path,exist_ok=True)

В Вместо python2, ОС.makedirs не'поддержка t параметр exist_ok. Вы можете использовать этот подход в Хейкки-Тойвонен'ы ответ:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise
Комментарии (0)

Вы можете использовать mkpath

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

Обратите внимание, что это создаст каталоги предка, а также.

Это работает для Python 2 и 3.

Комментарии (2)

Для однострочное решение, вы можете использовать IPython.utils.path.ensure_dir_exists():

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

В документации: Ensure, что существует каталог. Если он не существует, попробуйте создать его и защитить от гонки, если другой процесс делает то же самое.

Комментарии (3)

Я использую ОС.путь.существует()`, здесь это скрипт на Python 3, который может использоваться, чтобы проверить, если каталог существует, создайте его, если его не существует, и удалить его, если он существует (при желании).

Он предлагает пользователю для ввода директории и могут быть легко изменены.

Комментарии (0)

Вы можете использовать `ОС.listdir для этого:

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')
Комментарии (1)

Я видел Хейкки Тойвонен и А-Б-Б's отвечает и думал о этом варианте.

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST or not os.path.isdir(path):
            raise
Комментарии (0)

Я нашел этот Q/A и я был поначалу озадачен некоторые сбои и ошибки я получаю. Я работаю в Python 3 (в. 3.5 в виртуальной Анаконда среды на систему Arch Linux на архитектуру x86_64).

Рассмотрим эту структуру каталогов:

└── output/         ## dir
   ├── corpus       ## file
   ├── corpus2/     ## dir
   └── subdir/      ## dir

Вот мои эксперименты/ноты, которая многое объясняет:

# ----------------------------------------------------------------------------
# [1] https://stackoverflow.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist

import pathlib

""" Notes:
        1.  Include a trailing slash at the end of the directory path
            ("Method 1," below).
        2.  If a subdirectory in your intended path matches an existing file
            with same name, you will get the following error:
            "NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:

# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but no file created (os.makedirs creates dir, not files!  ;-)
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# [2] https://docs.python.org/3/library/os.html#os.makedirs

# Uncomment these to run "Method 1":

#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)

# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## works
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## works
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# Uncomment these to run "Method 2":

#import os, errno
#try:
#       os.makedirs(out_dir)
#except OSError as e:
#       if e.errno != errno.EEXIST:
#               raise
# ----------------------------------------------------------------------------

Вывод: на мой взгляд, "и Метод 2" и является более надежной.

[1] https://stackoverflow.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist

[2] https://docs.python.org/3/library/os.html#os.makedirs

Комментарии (0)

Если вы рассмотрите следующее:

os.path.isdir('/tmp/dirname')

значит, существует каталог (путь) и является каталогом. Так что для меня этот способ делает то, что мне нужно. Так что я могу убедиться, что это папка (а не файл) и существует.

Комментарии (1)