Функциональность 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
Как использовать для поиска файлов рекурсивно?
Какова лучшая структура проекта для Python-приложения? [закрыто]