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


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


2893


источник


Ответы:


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

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

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


3572



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не полагается на такой кеш. Это ограничение может быть в порядке для некоторых приложений.


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


750



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


570



I would personally recommend that you use os.path.isdir() to test instead of 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

If you have:

>>> dir = raw_input(":: ")

And a foolish user input:

:: /tmp/dirname/filename.etc

... You're going to end up with a directory named filename.etc when you pass that argument to os.makedirs() if you test with os.path.exists().


84



Check os.makedirs: (It makes sure the complete path exists.)
To handle the fact the directory might exist, catch OSError. (If exist_ok is False (the default), an OSError is raised if the target directory already exists.)

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

56



Insights on the specifics of this situation

You give a particular file at a certain path and you pull the directory from the file path. Then after making sure you have the directory, you attempt to open a file for reading. To comment on this code:

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

We want to avoid overwriting the builtin function, dir. Also, filepath or perhaps fullfilepath is probably a better semantic name than filename so this would be better written:

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

Your end goal is to open this file, you initially state, for writing, but you're essentially approaching this goal (based on your code) like this, which opens the file for reading:

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

Assuming opening for reading

Why would you make a directory for a file that you expect to be there and be able to read?

Just attempt to open the file.

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

If the directory or file isn't there, you'll get an IOError with an associated error number: errno.ENOENT will point to the correct error number regardless of your platform. You can catch it if you want, for example:

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

Assuming we're opening for writing

This is probably what you're wanting.

In this case, we probably aren't facing any race conditions. So just do as you were, but note that for writing, you need to open with the w mode (or a to append). It's also a Python best practice to use the context manager for opening files.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

However, say we have several Python processes that attempt to put all their data into the same directory. Then we may have contention over creation of the directory. In that case it's best to wrap the makedirs call in a try-except block.

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)

28



Starting from Python 3.5, pathlib.Path.mkdir has an exist_ok flag:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

This recursively creates the directory and does not raise an exception if the directory already exists.

(just as os.makedirs got an exists_ok flag starting from python 3.2).


28



I have put the following down. It's not totally foolproof though.

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

Now as I say, this is not really foolproof, because we have the possiblity of failing to create the directory, and another process creating it during that period.


22



Try the os.path.exists function

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

21



Check if a directory exists and create it if necessary?

The direct answer to this is, assuming a simple situation where you don't expect other users or processes to be messing with your directory:

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

or if making the directory is subject to race conditions (i.e. if after checking the path exists, something else may have already made it) do this:

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

But perhaps an even better approach is to sidestep the resource contention issue, by using temporary directories via tempfile:

import tempfile

d = tempfile.mkdtemp()

Here's the essentials from the online doc:

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.

New in Python 3.5: pathlib.Path with exist_ok

There's a new Path object (as of 3.4) with lots of methods one would want to use with paths - one of which is mkdir.

(For context, I'm tracking my weekly rep with a script. Here's the relevant parts of code from the script that allow me to avoid hitting Stack Overflow more than once a day for the same data.)

First the relevant imports:

from pathlib import Path
import tempfile

We don't have to deal with os.path.join now - just join path parts with a /:

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

Then I idempotently ensure the directory exists - the exist_ok argument shows up in Python 3.5:

directory.mkdir(exist_ok=True)

Here's the relevant part of the documentation:

If exist_ok is true, FileExistsError exceptions will be ignored (same behavior as the POSIX mkdir -p command), but only if the last path component is not an existing non-directory file.

Here's a little more of the script - in my case, I'm not subject to a race condition, I only have one process that expects the directory (or contained files) to be there, and I don't have anything trying to remove the directory.

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 objects have to be coerced to str before other APIs that expect str paths can use them.

Perhaps Pandas should be updated to accept instances of the abstract base class, os.PathLike.


15



In Python 3.4 you can also use the brand new pathlib module:

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.

14