Функциональность mkdir -p в Python
Заголовок: Как создать каталог и все недостающие родительские каталоги в Python без системного вызова?
Тело вопроса:
Есть ли способ получить функциональность, аналогичную команде mkdir -p
в оболочке, внутри Python? Я ищу решение, отличное от системного вызова. Я уверен, что код займёт менее 20 строк, и мне интересно, есть ли уже готовое решение.
5 ответ(ов)
Это проще, чем ловить исключение:
import os
if not os.path.exists(...):
os.makedirs(...)
Предупреждение Этот подход требует двух системных вызовов, что делает его более подверженным гонкам в определённых средах/условиях. Если вы пишете что-то более сложное, чем простой скрипт, работающий в контролируемой среде, лучше воспользоваться принятым ответом, который требует только одного системного вызова.
UPDATE 2012-07-27
Мне хочется удалить этот ответ, но я думаю, что в комментариях ниже есть полезная информация. Поэтому я преобразую его в вики.
Как упомянуто в других ответах, нам нужно иметь возможность обращения к файловой системе один раз, имитируя поведение команды mkdir -p
. Я не думаю, что это можно сделать полностью, но мы должны подойти к этому как можно ближе.
Сначала код, потом объяснение:
import os
import errno
def mkdir_p(path):
""" 'mkdir -p' в Python """
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
Как указывают комментарии к ответу @tzot, существуют проблемы с проверкой возможности создания директории до того, как мы на самом деле её создадим: нельзя точно сказать, изменилось ли состояние файловой системы за это время. Это также соответствует стилю Python — лучше просить прощения, чем разрешения.
Поэтому первое, что мы должны сделать, — это попытаться создать директорию, а если это не удастся, разобраться, в чем причина.
Как отмечает Якоб Габриэльсон, один из случаев, который мы должны учитывать, — это ситуация, когда файл уже существует в месте, где мы пытаемся создать директорию.
При выполнении mkdir -p
:
$ touch /tmp/foo
$ mkdir -p /tmp/foo
mkdir: cannot create directory '/tmp/foo': File exists
Аналогичное поведение в Python заключалось бы в возбуждении исключения.
Таким образом, нам нужно выяснить, так ли это. К сожалению, мы не можем этого сделать. Мы получаем одно и то же сообщение об ошибке от makedirs
, независимо от того, существует ли директория (хорошо) или файл, который мешает созданию директории (плохо).
Единственный способ выяснить, что произошло, — это снова проверить файловую систему, чтобы увидеть, есть ли там директория. Если да, то просто возвращаемся, иначе возбуждаем исключение.
Единственная проблема в том, что состояние файловой системы может измениться с момента вызова makedirs
. Например, файл мог существовать и вызывать сбой makedirs
, но теперь на его месте находится директория. Это не так уж критично, поскольку функция выйдет без исключения только в случае, если на момент последнего обращения к файловой системе директория уже существовала.
Я думаю, что ответ Асы в основном правильный, но вы можете немного расширить его, чтобы он работал больше как mkdir -p
. Вот два варианта:
Первый вариант:
import os
def mkdir_path(path):
if not os.access(path, os.F_OK):
os.makedirs(path)
Во втором варианте используется обработка исключений:
import os
import errno
def mkdir_path(path):
try:
os.makedirs(path)
except OSError as e:
if e.errno != errno.EEXIST:
raise
Оба этих подхода обрабатывают случай, когда путь уже существует, не выдавая ошибок, но при этом позволяют другим ошибкам всплывать.
Функция mkdir_p
используется для создания директории по указанному пути, если она не существует. Вот исправленный и немного улучшенный код:
import os
def mkdir_p(filename):
try:
folder = os.path.dirname(filename)
if not os.path.exists(folder):
os.makedirs(folder)
return True
except Exception as e:
print(f"Error occurred: {e}") # Рекомендуется выводить информацию об ошибке
return False
Использование функции:
filename = "./download/80c16ee665c8/upload/backup/mysql/2014-12-22/adclient_sql_2014-12-22-13-38.sql.gz"
if mkdir_p(filename):
print("Created dir: %s" % (os.path.dirname(filename)))
Обратите внимание на несколько моментов:
- Лучше указывать конкретный тип исключения, который вы хотите обрабатывать, или по крайней мере использовать
Exception
, как показано в примере, чтобы лучше отлавливать возможные ошибки. - В Python 3 следует использовать
print()
как функцию, поэтому заменитеprint "Created dir :%s" % ...
наprint("Created dir: %s" % ...)
. - Убедитесь, что ваша переменная
filename
корректно содержит путь к файлу, а не к директории, чтобы всё работало как задумано.
Судя по вашему коду, вы пытаетесь создать временную директорию и сразу же её удалить. Однако, ваш код вызывает ошибку, и вот почему.
Функция
tempfile.mktemp(dir=path)
возвращает строку с путем к временной директории, но при этом переменнаяpath
должна быть заранее определена. Убедитесь, чтоpath
задана до вызова этой функции, иначе вы получите ошибкуNameError
.После создания пути с помощью
tempfile.mktemp()
, вы пытаетесь создать директорию с помощьюos.makedirs(path)
. Но если вы сразу же вызываетеos.rmdir(path)
, то это приведет к ошибке, если директория не пуста.os.rmdir()
может удалить только пустую директорию.
Следовательно, вот исправленный пример кода, который сначала создаст временную директорию и затем её удалит:
import os
import tempfile
import shutil
# Создаем временную директорию
path = tempfile.mkdtemp() # mkdtemp создает и возвращает путь к директории
print(f"Создана временная директория: {path}")
# Ваша логика работы с временной директорией здесь
# Удаляем временную директорию с её содержимым
shutil.rmtree(path) # rmtree удаляет директорию и все её содержимое
print(f"Удалена временная директория: {path}")
Используя tempfile.mkdtemp()
, вы создаете временную директорию, которую затем можно безопасно удалить с помощью shutil.rmtree()
. Это должно решить вашу проблему.
Как создать каталог и все отсутствующие родительские каталоги?
Извлечение имени файла из пути независимо от формата ОС/пути
Как получить абсолютный путь к файлу в Python
Как использовать для поиска файлов рекурсивно?
Какова лучшая структура проекта для Python-приложения? [закрыто]