Как создать каталог, если он не существует?

2448
голосов

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

задан Parand 07 нояб. '08 в 21:56
источник

24 ответов

2947
голосов

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

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

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

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

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

import os, errno

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

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

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

ответ дан Blair Conrad 07 нояб. '08 в 22:06
источник
558
голосов

Использование 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 17 февр. '11 в 20:17
источник
530
голосов

Python 3.5 +:

Кредит: ответ hiro protagonist

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

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

Python 3.2 +:

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

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

Python 2.7 +:

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

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

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


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

ответ дан A-B-B 16 янв. '13 в 20:31
источник
78
голосов

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

ответ дан crimsonstone 14 янв. '09 в 20:57
источник
49
голосов

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

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
ответ дан Douglas Mayle 07 нояб. '08 в 22:01
источник
25
голосов

Основные сведения об этой ситуации

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

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 23 янв. '15 в 2:49
источник
22
голосов

Я поставил следующее. Однако он не полностью уверен в надежности.

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 08 нояб. '08 в 0:23
источник
20
голосов

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

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

ответ дан hiro protagonist 14 дек. '16 в 19:06
источник
16
голосов

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

if not os.path.exists(dir):
    os.mkdir(dir)
ответ дан gone 07 нояб. '08 в 22:00
источник
13
голосов

В 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 11 марта '15 в 23:50
источник
12
голосов

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

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

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.

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

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

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

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

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 23 янв. '15 в 2:45
источник
5
голосов

Вы можете использовать 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 14 сент. '16 в 0:44
источник
5
голосов

соответствующая документация 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 14 июля '14 в 18:29
источник
2
голосов

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

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

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

ответ дан tashuhka 29 марта '16 в 18:50
источник
2
голосов

Я видел Heikki Toivonen и 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
ответ дан alissonmuller 08 февр. '16 в 7:08
источник
2
голосов

В 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
ответ дан euccas 04 янв. '17 в 1:33
источник
1
голос

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

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')
ответ дан iPhynx 08 июня '16 в 15:52
источник
1
голос

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

ответ дан Michael Strobel 09 февр. '17 в 6:56
источник
1
голос

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

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

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

try:
    os.makedirs(dir_path)
except OSError as e:
    if e.errno == errno.EEXIS:
        pass
    else:
        raise
ответ дан SHAHS 19 окт. '17 в 17:09
источник
0
голосов
import os
os.system("mkdir -p ./testdir")

-p делает всю работу за вас, зачем заниматься этим приложением try/catch, когда у linux и windows powershell есть mkdir -p в течение многих лет. Единственная причина, по которой нужно выполнить проверку, - это если вы хотите распечатать информацию в журнале или экране.

ответ дан Eamonn Kenny 29 авг. '16 в 14:34
источник
0
голосов

Как насчет этого?

import os
import errno

def make_sure_path_exists(path):
try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
ответ дан Akash Nayak 15 нояб. '17 в 13:33
источник
0
голосов

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

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

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

ответ дан Ralph Schwerdt 03 дек. '16 в 19:00
источник
-1
голос
import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

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

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

ответ дан Evil Exists 06 июля '17 в 2:15
источник
-2
голосов
import os

if not os.path.isfile("test") and not os.path.isdir("test"):
    os.mkdir("test")
ответ дан c4f4t0r 02 сент. '13 в 0:04
источник

Другие вопросы по меткам