it-swarm.com.ru

Как я могу безопасно создать вложенный каталог в 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)

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

3366
Parand

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

Попробуйте 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 и изучить встроенный код ошибки (см. Существует ли кроссплатформенный способ получения информации из ошибки Python OSError ):

import os, errno

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

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

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

Современные версии Python немного улучшают этот код, предоставляя FileExistsError (в 3.3+) ...

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

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

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
4171
Blair Conrad

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


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

1010
A-B-B

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

589
Heikki Toivonen

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

92
crimsonstone

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

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

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

47
hiro protagonist

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

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

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)
36
Aaron Hall

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

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

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

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

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

27
Ali Afshar

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

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

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.

20
Aaron Hall

В 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.
17
Antti Haapala

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

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

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

12
kavadias

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

os.makedirs(path,exist_ok=True)

В Python2os.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
euccas

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

8
Dennis Golomazov

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

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

7
Michael Strobel

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

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

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

7
tashuhka

Я видел ответы 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
6
alissonmuller

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

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

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

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

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

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')
6
iPhynx

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

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

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

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

3
Ralph Schwerdt

Используйте библиотеку pathlib

import pathlib
mypath="/my/directory"
pathlib.Path(mypath).mkdir(parents=True, exist_ok=True) 
2
Hasee Amarathunga

Вызовите функцию 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')
2
Steffi Keran Rani J

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

 if not os.path.isdir(test_img_dir):
     os.mkdir(str("./"+test_img_dir))
1
Manivannan Murugavel

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

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)

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

0
Hussam Kurd
import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

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

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

0
Evil Exists

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

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

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

0
Geoff Paul Bremner