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

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

4534
Parand 7 Ноя 2008 в 21:56

25 ответов

Лучший ответ

На Python ≥ 3.5 используйте pathlib.Path.mkdir:

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

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

Попробуйте 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 значительно улучшают этот код, предоставляя {{X0} } (в 3.3 +) ...

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

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

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
4957
Boris 30 Дек 2019 в 09:23

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

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

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 имеет значение true, исключения 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.

24
Aaron Hall 7 Июл 2017 в 03:19

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

 if not os.path.isdir(test_img_dir):
     os.mkdir(test_img_dir)
5
Jean-François Fabre 16 Май 2019 в 19:07

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

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

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

4
Ralph Schwerdt 3 Дек 2016 в 16:35
import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

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

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

1
Evil Exists 5 Июл 2017 в 23:15

Почему бы не использовать модуль подпроцесса, если он работает на компьютере, который поддерживает команду mkdir с опцией -p? Работает на Python 2.7 и Python 3.6

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

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

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

Если вам нужна обработка ошибок:

from subprocess import check_call
try:
    check_call(['mkdir', '-p', 'path1/path2/path3'])
except:
    handle...
5
Jean-François Fabre 16 Май 2019 в 19:37

Вы должны установить полный путь перед созданием каталога:

import os,sys,inspect
import pathlib

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
your_folder = currentdir + "/" + "your_folder"

if not os.path.exists(your_folder):
   pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)

Это работает для меня и, надеюсь, это будет работать и для вас

3
Hussam Kurd 19 Май 2019 в 00:40

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

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')
6
dippas 30 Сен 2017 в 12:31

Попробуйте {{ X0}} функция

if not os.path.exists(dir):
    os.mkdir(dir)
32
jesterjunk 15 Окт 2015 в 16:05

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)

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


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

1214
Acumenus 17 Май 2018 в 17:16

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

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

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

13
Peter Mortensen 31 Дек 2017 в 03:12

В 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.
20
Antti Haapala 11 Мар 2015 в 20:50

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

101
Peter Mortensen 31 Май 2014 в 14:06

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

os.makedirs(path,exist_ok=True)

В Python2 os.makedirs не поддерживает настройку exist_ok. Вы можете использовать этот подход в ответе heikki-toivonen:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise
10
Community 23 Май 2017 в 12:26

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

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

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-Кроме.

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)
44
Aaron Hall 1 Апр 2016 в 21:54

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

9
Dennis Golomazov 13 Сен 2016 в 21:44

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

602
vallentin 24 Апр 2017 в 01:55

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

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
78
Georgy 16 Май 2019 в 12:15

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

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

8
Peter Mortensen 31 Дек 2017 в 03:16

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

11
Georgy 16 Май 2019 в 12:25

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

6
Victoria Stuart 16 Дек 2017 в 19:26

Я изложил следующее. Это не совсем надежно, хотя.

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

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

29
Peter Mortensen 31 Май 2014 в 14:05

Начиная с 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 флаг exist_ok, начиная с Python 3.2, например os.makedirs(path, exist_ok=True))

69
Viliami 17 Дек 2018 в 07:05

Вызовите функцию 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')
3
Steffi Keran Rani J 28 Апр 2018 в 16:00