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

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

Есть ли флаг для «open», что делает это автоматически?

вопрос задан 7.11.2008
Parand
38009 репутация

25 ответов


  • 3811 рейтинг

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

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

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

    ответ дан Blair Conrad, с репутацией 145093, 7.11.2008
  • 859 рейтинг

    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, так и на 3.

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

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


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

    ответ дан A-B-B, с репутацией 20353, 16.01.2013
  • 580 рейтинг

    Использование 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 (Permission denied, error 13).

    ответ дан Heikki Toivonen, с репутацией 20526, 17.02.2011
  • 89 рейтинг

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

    ответ дан Ali Afshar, с репутацией 32346, 14.01.2009
  • 59 рейтинг

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

     import os
    try:
        os.makedirs('./path/to/somewhere')
    except OSError:
        pass
     
    ответ дан Douglas Mayle, с репутацией 12811, 7.11.2008
  • 32 рейтинг

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

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

     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)
     
    ответ дан Aaron Hall, с репутацией 153756, 22.01.2015
  • 30 рейтинг

    Начиная с 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).

    ответ дан hiro protagonist, с репутацией 15882, 14.12.2016
  • 24 рейтинг

    Я поставил следующее. Однако это не совсем безопасно.

     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
     

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

    ответ дан Ali Afshar, с репутацией 32346, 7.11.2008
  • 24 рейтинг

    Попробуйте функцию os.path.exists

     if not os.path.exists(dir):
        os.mkdir(dir)
     
    ответ дан gone, с репутацией 2427, 7.11.2008
  • 17 рейтинг

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

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

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

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

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

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

    ответ дан Aaron Hall, с репутацией 153756, 22.01.2015
  • 15 рейтинг

    В 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.
     
    ответ дан Antti Haapala, с репутацией 74528, 11.03.2015
  • 9 рейтинг

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

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

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

    ответ дан kavadias, с репутацией 479, 14.07.2014
  • 7 рейтинг

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

    ответ дан Dennis Golomazov, с репутацией 7413, 13.09.2016
  • 6 рейтинг

    В Python3 os.makedirs поддерживает установку exist_ok . Значение по умолчанию - False , что означает, что OSError будет поднят, если целевой каталог уже существует. Установив от exist_ok до 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
     
    ответ дан euccas, с репутацией 376, 3.01.2017
  • 5 рейтинг

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

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

    ответ дан Michael Strobel, с репутацией 124, 9.02.2017
  • 5 рейтинг

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

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

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

    ответ дан tashuhka, с репутацией 3209, 29.03.2016
  • 4 рейтинг

    Я видел ответы Хейкки Тойвонен и АББ и думал об этом варианте.

     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
     
    ответ дан alissonmuller, с репутацией 102, 8.02.2016
  • 4 рейтинг

    Я нашел этот 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

    ответ дан Victoria Stuart, с репутацией 1008, 16.12.2017
  • 3 рейтинг

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

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

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

     try:
        os.makedirs(dir_path)
    except OSError as e:
        if e.errno != errno.EEXIS:
            raise
     
    ответ дан SHAHS, с репутацией 366, 19.10.2017
  • 3 рейтинг

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

     import os
    if 'dirName' in os.listdir('parentFolderPath')
        print('Directory Exists')
     
    ответ дан iPhynx, с репутацией 300, 8.06.2016
  • 1 рейтинг

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

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

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

    ответ дан Ralph Schwerdt, с репутацией 41, 3.12.2016
  • 0 рейтинг

    Используйте эту команду для проверки и создания каталога

      if not os.path.isdir(test_img_dir):
         os.mkdir(str("./"+test_img_dir))
     
    ответ дан Manivannan Murugavel, с репутацией 192, 16.04.2018
  • 0 рейтинг

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

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

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

    ответ дан Geoff Paul Bremner, с репутацией 46, 11.09.2018
  • 0 рейтинг

    Вызовите функцию 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')
     
    ответ дан Steffi Keran Rani J, с репутацией 705, 28.04.2018
  • -2 рейтинг
     import os
    if os.path.isfile(filename):
        print "file exists"
    else:
        "Your code here"
     

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

    Это будет проверять, есть ли файл, если он нет, тогда он его создаст.

    ответ дан Evil Exists, с репутацией 9, 5.07.2017