10

Функциональность mkdir -p в Python

12

Заголовок: Как создать каталог и все недостающие родительские каталоги в Python без системного вызова?

Тело вопроса:

Есть ли способ получить функциональность, аналогичную команде mkdir -p в оболочке, внутри Python? Я ищу решение, отличное от системного вызова. Я уверен, что код займёт менее 20 строк, и мне интересно, есть ли уже готовое решение.

5 ответ(ов)

1

Это проще, чем ловить исключение:

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

Предупреждение Этот подход требует двух системных вызовов, что делает его более подверженным гонкам в определённых средах/условиях. Если вы пишете что-то более сложное, чем простой скрипт, работающий в контролируемой среде, лучше воспользоваться принятым ответом, который требует только одного системного вызова.

UPDATE 2012-07-27

Мне хочется удалить этот ответ, но я думаю, что в комментариях ниже есть полезная информация. Поэтому я преобразую его в вики.

0

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

0

Я думаю, что ответ Асы в основном правильный, но вы можете немного расширить его, чтобы он работал больше как 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

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

0

Функция 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)))

Обратите внимание на несколько моментов:

  1. Лучше указывать конкретный тип исключения, который вы хотите обрабатывать, или по крайней мере использовать Exception, как показано в примере, чтобы лучше отлавливать возможные ошибки.
  2. В Python 3 следует использовать print() как функцию, поэтому замените print "Created dir :%s" % ... на print("Created dir: %s" % ...).
  3. Убедитесь, что ваша переменная filename корректно содержит путь к файлу, а не к директории, чтобы всё работало как задумано.
0

Судя по вашему коду, вы пытаетесь создать временную директорию и сразу же её удалить. Однако, ваш код вызывает ошибку, и вот почему.

  1. Функция tempfile.mktemp(dir=path) возвращает строку с путем к временной директории, но при этом переменная path должна быть заранее определена. Убедитесь, что path задана до вызова этой функции, иначе вы получите ошибку NameError.

  2. После создания пути с помощью 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(). Это должно решить вашу проблему.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь