Как я могу безопасно создать вложенный каталог?
Какой самый элегантный способ проверить, существует ли каталог, в который будет записываться файл, а если нет, создайте каталог с помощью 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", что делает это автоматически?
Ответы
Ответ 1
Я вижу два ответа с хорошими качествами, каждый с небольшим недостатком, поэтому я дам свой взгляд на него:
Попробуйте os.path.exists
и рассмотрите os.makedirs
для создания.
import os
if not os.path.exists(directory):
os.makedirs(directory)
Как отмечено в комментариях и в других местах, существует условие os.path.exists
- если каталог создается между 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
, но предположим, что другой создал каталог после первой проверки, а затем удалил его до второй проверки - нас все еще можно обмануть.
В зависимости от приложения опасность одновременных операций может быть больше или меньше опасности других факторов, таких как права доступа к файлам. Разработчик должен знать больше о конкретном разрабатываемом приложении и его ожидаемой среде, прежде чем выбирать реализацию.
Современные версии 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.
Ответ 2
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
, errno.EEXIST
как для файлов, так и для каталогов. Более надежно просто проверить, существует ли каталог.
Альтернатива:
mkpath
создает вложенный каталог и ничего не делает, если каталог уже существует. Это работает как на Python 2, так и на 3.
import distutils.dir_util
distutils.dir_util.mkpath(path)
Per Bug 10948, серьезным ограничением этой альтернативы является то, что она работает только один раз на процесс python для заданного пути. Другими словами, если вы используете его для создания каталога, затем удалите каталог изнутри или за пределами Python, затем снова используйте mkpath
для воссоздания одного и того же каталога, mkpath
просто молча использует свою недопустимую кешированную информацию о том, что ранее создала каталог, и на самом деле не сделает каталог снова. Напротив, os.makedirs
не полагается на такой кеш. Это ограничение может быть в порядке для некоторых приложений.
Что касается режима каталога, обратитесь к документации, если вам это нравится.
Ответ 3
Использование 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).
Ответ 4
Я лично рекомендовал бы использовать 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()
.
Ответ 5
Проверьте os.makedirs
: (он удостоверяется, что существует полный путь.)
Чтобы справиться с тем фактом, что каталог может существовать, перехватите OSError
.
(Если exist_ok
- False
(по умолчанию), то OSError
вызывается, если целевой каталог уже существует.)
import os
try:
os.makedirs('./path/to/somewhere')
except OSError:
pass
Ответ 6
Начиная с 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)
)
Ответ 7
Основные сведения об этой ситуации
Вы указываете конкретный файл по определенному пути, и вы вытаскиваете каталог из пути к файлу. Затем, убедившись, что у вас есть каталог, вы пытаетесь открыть файл для чтения. Чтобы прокомментировать этот код:
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)
Ответ 8
Попробуйте os.path.exists
функция
if not os.path.exists(dir):
os.mkdir(dir)
Ответ 9
Я поставил следующее. Однако он не полностью уверен в надежности.
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
Теперь, как я уже сказал, это не является действительно надежным, потому что у нас есть возможность не создавать каталог и другой процесс, создающий его в течение этого периода.
Ответ 10
Проверить, существует ли каталог и создать его при необходимости?
Прямым ответом на это является принятие простой ситуации, когда вы не ожидаете, что другие пользователи или процессы будут входить в ваш каталог:
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
.
Ответ 11
В 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
В соответствующей документации по 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
Документация предполагает это именно из-за состояния гонки, обсуждаемого в этом вопросе. Кроме того, как другие упоминают здесь, есть преимущество в производительности при запросе один раз, а не дважды ОС. Наконец, выдвигаемый аргумент, потенциально в пользу второго кода, в некоторых случаях --when разработчик знает среду, в которой находится приложение running--, может быть защищен только в особом случае, когда программа создала частную среду для сам (и другие экземпляры той же программы).
Даже в этом случае это плохая практика и может привести к длительной бесполезной отладке. Например, тот факт, что мы устанавливаем права доступа для каталога, не должен оставлять нас с впечатлением, что права доступа установлены для наших целей. Родительский каталог может быть смонтирован с другими разрешениями. В общем, программа должна всегда работать правильно, и программист не должен ожидать одну конкретную среду.
Ответ 13
В 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
Ответ 14
Вы можете использовать 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.
Ответ 15
Для однострочного решения вы можете использовать IPython.utils.path.ensure_dir_exists()
:
from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)
Из документации: убедитесь, что каталог существует. Если он не существует, попытайтесь создать его и защитить от состояния гонки, если другой процесс делает то же самое.
Ответ 16
Я использую os.path.exists()
, вот скрипт на Python 3, который можно использовать для проверки, существует ли каталог, создать его, если он не существует, и удалить его, если он существует (при желании).
Он предлагает пользователям ввести каталог и может быть легко изменен.
Ответ 17
Я видел ответы Хейкки Тойвонен и АББ и думал об этом варианте.
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
Ответ 18
Вы можете использовать os.listdir
для этого:
import os
if 'dirName' in os.listdir('parentFolderPath')
print('Directory Exists')
Ответ 19
При работе с файловыми вводами-выводами важно рассмотреть
TOCTTOU (время проверки на время использования)
Таким образом, проверка с помощью if
и последующего чтения или записи позже может закончиться необработанным исключением ввода-вывода. Лучший способ сделать это:
try:
os.makedirs(dir_path)
except OSError as e:
if e.errno != errno.EEXIS:
raise
Ответ 20
Я нашел этот Q/A, и изначально я был озадачен некоторыми неудачами и ошибками, которые я получал. Я работаю в Python 3 (v.3.5 в виртуальной среде Anaconda в системе Arch Linux x86_64).
Рассмотрим эту структуру каталогов:
└── output/ ## dir
├── corpus ## file
├── corpus2/ ## dir
└── subdir/ ## dir
Вот мои эксперименты/заметки, в которых разъясняются вещи:
# ----------------------------------------------------------------------------
# [1] /questions/340/how-to-check-if-a-directory-exists-and-create-it-if-necessary
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
Ответ 21
Если вы считаете следующее:
os.path.isdir('/tmp/dirname')
означает, что существует каталог (путь) AND - это каталог. Поэтому для меня этот путь делает то, что мне нужно. Поэтому я могу убедиться, что это папка (а не файл) и существует.
Ответ 22
Почему бы не использовать модуль подпроцесса, если он работает на компьютере, который поддерживает команду 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...
Ответ 23
Используйте эту команду, проверьте и создайте каталог
if not os.path.isdir(test_img_dir):
os.mkdir(test_img_dir)
Ответ 24
Вызовите функцию 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')
Ответ 25
Используйте библиотеку pathlib
import pathlib
mypath="/my/directory"
pathlib.Path(mypath).mkdir(parents=True, exist_ok=True)
Ответ 26
import os
if os.path.isfile(filename):
print "file exists"
else:
"Your code here"
Где ваш код здесь используется командой (touch)
Это будет проверять, есть ли файл, если он нет, тогда он его создаст.
Ответ 27
Вы должны установить полный путь перед созданием каталога:
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)
Это работает для меня и, надеюсь, это будет работать и для вас
Ответ 28
import os
os.system("mkdir -p ./testdir")
-p делает всю работу за вас, зачем заниматься этим приложением try/catch, когда у linux и windows powershell есть mkdir -p в течение многих лет. Единственная причина, по которой нужно выполнить проверку, - это если вы хотите распечатать информацию в журнале или экране.
Ответ 29
Как насчет этого?
import os
import errno
def make_sure_path_exists(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
Ответ 30
import os
if not os.path.isfile("test") and not os.path.isdir("test"):
os.mkdir("test")