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

каков самый элегантный способ проверить, существует ли каталог, в который будет записан файл, и если нет, создайте каталог с помощью 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, Блэр, и Дуглас). Вот что у меня теперь:--4-->

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

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

25 ответов


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

попробовать 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 3.5+:

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

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

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

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


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


использование 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

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


Я бы лично рекомендовал вам использовать os.path.isdir() проверить вместо os.path.exists().

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

... Вы получите каталог с именем filename.etc когда вы передаете этот аргумент os.makedirs() если вы испытываете с os.path.exists().


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

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass

понимание специфики этой ситуации

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

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

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

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

ваша конечная цель-открыть этот файл, который вы изначально указываете, для записи, но вы по существу приближаетесь к этой цели (на основе вашего кода), как это, который открывает файл для чтение:

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

предполагая открытие для чтения

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

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

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

если каталог или файл не существует, вы получите IOError С соответствующим номером ошибки: errno.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 режим (или a добавить). Также рекомендуется использовать менеджер контекста для открытия файлов на Python.

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)

начиная с Python 3.5, pathlib.Path.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)

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

(как и os.makedirs получил exists_ok флаг начиная с Python 3.2).


попробовать os.path.exists функции

if not os.path.exists(dir):
    os.mkdir(dir)

Я поставил следующее. Хотя это не совсем надежно.

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

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


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

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

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

вот основы из онлайн-документа:

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

новое в Python 3.5:pathlib.Path С exist_ok

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

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

сначала соответствующий импорт:

from pathlib import Path
import tempfile

мы не должны иметь дело с os.path.join теперь-просто присоединиться к части пути с /:

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

тогда я идемпотентно гарантирую, что каталог существует -exist_ok аргумент отображается в Python 3.5:

directory.mkdir(exist_ok=True)

вот часть документация:

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

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

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

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

возможно, панды должны быть обновлены, чтобы принимать экземпляры абстрактного базового класса,os.PathLike.


в Python 3.4 вы также можете использовать новое pathlib модуль:

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.

на соответствующая документация 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

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

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


можно использовать 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.


на Питон3, os.makedirs поддерживает задание exist_ok. Значение по умолчанию -False, что означает OSError будет поднят, если целевой каталог уже существует. Установив exist_ok to True, OSError (каталог существует) будет проигнорирован, и каталог не будет создан.

os.makedirs(path,exist_ok=True)

на вместо python2, os.makedirs не поддерживает задание 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

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

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

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


Я использую os.path.exists(), здесь - это скрипт Python 3, который можно использовать для проверки, существует ли каталог, создать его, если он не существует, и удалить его, если он существует (при желании).

он запрашивает пользователей для ввода каталога и может быть легко изменен.


Я нашел этот Q / A, и я был первоначально озадачен некоторыми сбоями и ошибками, которые я получал. Я работаю в Python 3 (v.3.5 в виртуальной среде Anaconda в системе 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] как я могу создать каталог, если он не существовать?

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


Я видел Хейкки Тойвонен и A-B-Bответы и мысли об этой вариации.

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

можно использовать os.listdir для этого:

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')

при работе с файловым вводом-выводом важно учитывать

TOCTTOU (время проверки на время использования)

Так что проверка с if и затем чтение или запись позже может закончиться необработанным исключением ввода-вывода. Лучший способ сделать это:

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

Если вы считаете следующее:

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

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


используйте эту команду check и создайте dir

 if not os.path.isdir(test_img_dir):
     os.mkdir(str("./"+test_img_dir))

вызов функции create_dir() в точке входа программы/проекта.

import os

def create_dir(directory):
    if not os.path.exists(directory):
        print('Creating Directory '+directory)
        os.makedirs(directory)

create_dir('Project directory')

почему бы не использовать модуль подпроцесса, если он работает на компьютере, поддерживающем языки оболочки? Работает на python 2.7 и python 3.6

from subprocess import call
call(['mkdir', '-p', 'path1/path2/path3'])

должны сделать трюк на большинстве систем.


import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

где ваш код здесь, используйте команду (touch)

это проверит, есть ли файл, если его нет, то он создаст его.