Как я могу безопасно создать вложенный каталог? - PullRequest
3590 голосов
/ 07 ноября 2008

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

Ответы [ 26 ]

19 голосов
/ 11 марта 2015

В 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.
12 голосов
/ 14 июля 2014

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

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

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

9 голосов
/ 04 января 2017

In 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
8 голосов
/ 14 сентября 2016

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

7 голосов
/ 09 февраля 2017

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

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

7 голосов
/ 29 марта 2016

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

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

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

6 голосов
/ 08 февраля 2016

Я видел Хейкки Тойвонен и 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
6 голосов
/ 08 июня 2016

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

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')
6 голосов
/ 16 декабря 2017

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

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

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

Вот мои эксперименты / заметки, которые проясняют вещи:

# ----------------------------------------------------------------------------
# [1] /175811/kak-ya-mogu-bezopasno-sozdat-vlozhennyi-katalog

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

6 голосов
/ 19 октября 2017

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

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

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

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