Как удалить / удалить папку, которая не пуста?

847

Я получаю сообщение об ошибке «Отказано в доступе» при попытке удалить не пустую папку. Я использовал следующую команду в мою попытку: os.remove("/folder_name").

Каков наиболее эффективный способ удаления / удаления папки / каталога, который не является пустым?

Amara
источник
32
Также обратите внимание, что даже если каталог был пуст, os.remove снова потерпит неудачу, потому что правильная функция - os.rmdir.
tzot
А о конкретном rm -rfповедении смотрите: stackoverflow.com/questions/814167/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:

1350
import shutil

shutil.rmtree('/folder_name')

Ссылка на стандартную библиотеку: shutil.rmtree .

По замыслу, происходит rmtreeсбой в деревьях папок, содержащих файлы только для чтения. Если вы хотите, чтобы папка была удалена независимо от того, содержит ли она файлы только для чтения, используйте

shutil.rmtree('/folder_name', ignore_errors=True)
ddaa
источник
73
Обратите внимание, что rmtreeпроизойдет сбой, если есть файлы только для чтения: stackoverflow.com/questions/2656322/…
Шридхар Ратнакумар
9
Это не работает для меня: Traceback (последний вызов был последним): файл "foo.py", строка 31, в файле <module> shutil.rmtree (thistestdir) "/usr/lib/python2.6/shutil.py ", строка 225, в файле ошибки rmtree (os.rmdir, path, sys.exc_info ()) Файл" /usr/lib/python2.6/shutil.py ", строка 223, в файле rmtree os.rmdir (путь) OSError: [Errno 90] Директория не пуста: '/ path / to / rmtree'
Клейтон Хьюз,
4
Клейтон: по всей вероятности, файл был добавлен одновременно, в то время как rmtree был занят удалением, «rm -rf» тоже не получится.
ddaa
14
Кто-нибудь знает, почему эта функциональность отсутствует в пакете os? Похоже, os.rmdir совершенно бесполезен. Есть ли веские аргументы в пользу того, почему это так реализовано?
Малкольм
21
@Malcolm Пакет является оберткой для функций ОС. В системах POSIX rmdir должен завершиться с ошибкой, если каталог не пустой. Ubuntu и Windows являются популярными примерами POSIX-совместимости в этом отношении.
Иэн Самуэль Маклин, старейшина
138

Из документации по питонуos.walk() :

# Delete everything reachable from the directory named in 'top',
# assuming there are no symbolic links.
# CAUTION:  This is dangerous!  For example, if top == '/', it
# could delete all your disk files.
import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))
kkubasik
источник
1
Ну, может я ошибаюсь в даундоминге. Но я могу, сейчас я думаю, что это правильно.
ddaa
3
@ddaa: Хотя использование shutil, безусловно, самый простой способ, в этом решении нет ничего лишнего. Я бы не проголосовал за этот ответ, но у меня есть только время, чтобы отменить ваше отрицательное голосование :)
Джереми Кантрелл
7
Сам код является питоническим. Использование его вместо shutil.rmtree в реальной программе было бы бессмысленным: это игнорировало бы «один очевидный способ сделать это». Во всяком случае, это семантика, устранение downmod.
ddaa
2
@ddaa Это бессмысленно хотеть регистрировать каждый файл или каталог, который удален? Я не уверен, как это сделать с shutil.rmtree?
Джонатан Комар
4
@ddaa Это была пища для размышлений, то есть риторика. Я знаю, что я делаю. Я просто подумал, что вы могли бы пересмотреть «очевидный способ сделать это», указав причину, по которой shutil.rmtree может не подходить.
Джонатан Комар
112
import shutil
shutil.rmtree(dest, ignore_errors=True)
Шива Мандади
источник
1
Это правильный ответ. В моей системе, даже если я установил все в определенной папке для записи-чтения, я получаю сообщение об ошибке при попытке удалить. ignore_errors=Trueрешает проблему.
Авентин
3
В моем ответе onerrorвместо параметра используется параметр ignore_errors. Таким образом, файлы только для чтения удаляются, а не игнорируются.
Дейв Чендлер
Да, это не удалит файлы при ошибке. Таким образом, в основном весь rmtree()метод игнорируется.
Юха Унтинен,
2
Это должно было быть небольшое изменение к ответу, принятому 6 годами ранее, скорее новый ответ. Я сделаю это сейчас.
Жан-Франсуа Корбетт
22

из python 3.4 вы можете использовать:

import pathlib

def delete_folder(pth) :
    for sub in pth.iterdir() :
        if sub.is_dir() :
            delete_folder(sub)
        else :
            sub.unlink()
    pth.rmdir() # if you just want to delete dir content, remove this line

где pthэто pathlib.Pathэкземпляр. Приятно, но, возможно, не самый быстрый.

Йота
источник
10

С docs.python.org :

В этом примере показано, как удалить дерево каталогов в Windows, где для некоторых файлов установлен бит только для чтения. Он использует обратный вызов onerror, чтобы очистить бит readonly и повторить попытку удаления. Любой последующий сбой будет распространяться.

import os, stat
import shutil

def remove_readonly(func, path, _):
    "Clear the readonly bit and reattempt the removal"
    os.chmod(path, stat.S_IWRITE)
    func(path)

shutil.rmtree(directory, onerror=remove_readonly)
Дэйв Чендлер
источник
7
import os
import stat
import shutil

def errorRemoveReadonly(func, path, exc):
    excvalue = exc[1]
    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
        # change the file to be readable,writable,executable: 0777
        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)  
        # retry
        func(path)
    else:
        # raiseenter code here

shutil.rmtree(path, ignore_errors=False, onerror=errorRemoveReadonly) 

Если установлено ignore_errors, ошибки игнорируются; в противном случае, если установлено значение onerror, оно вызывается для обработки ошибки с аргументами (func, path, exc_info), где func - это os.listdir, os.remove или os.rmdir; путь - это аргумент этой функции, который вызвал ее сбой; и exc_info - это кортеж, возвращаемый sys.exc_info (). Если ignore_errors - false, а onerror - None, возникает исключение. Введите здесь код

Р.Ю. Чжэн
источник
Согласно документации , исключения, возникшие из-за ошибки onerror, не будут обнаружены, поэтому я не уверен, что ваш код ввода рейза здесь что-то значит.
Кмарш
-1. Это кажется слишком сложным по сравнению с ответом Дэйва Чендлера. Также, если мы хотим удалить только для чтения, нам не нужно делать файлы исполняемыми.
19
7

Основываясь на ответе kkubasik, проверьте, существует ли папка перед удалением, более надежный

import shutil
def remove_folder(path):
    # check if folder exists
    if os.path.exists(path):
         # remove if exists
         shutil.rmtree(path)
    else:
         # throw your exception to handle this special scenario
         raise XXError("your exception") 
remove_folder("/folder_name")
Чарльз Чоу
источник
6
это вводит возможное состояние гонки
Кори Голдберг
1
в соответствии с наиболее подходящим способом удаления файла, который может не существовать , предпочтительнее tryудалить и обработать, exceptчем вызвать exists()первым
TT--
6

если вы уверены, что хотите удалить все дерево dir и больше не интересуетесь содержимым dir, то сканирование всего дерева dir - это глупость ... просто вызовите для этого команду родной ОС из python. Это будет быстрее, эффективнее и потребляет меньше памяти.

RMDIR c:\blah /s /q 

или * nix

rm -rf /home/whatever 

В Python код будет выглядеть так ...

import sys
import os

mswindows = (sys.platform == "win32")

def getstatusoutput(cmd):
    """Return (status, output) of executing cmd in a shell."""
    if not mswindows:
        return commands.getstatusoutput(cmd)
    pipe = os.popen(cmd + ' 2>&1', 'r')
    text = pipe.read()
    sts = pipe.close()
    if sts is None: sts = 0
    if text[-1:] == '\n': text = text[:-1]
    return sts, text


def deleteDir(path):
    """deletes the path entirely"""
    if mswindows: 
        cmd = "RMDIR "+ path +" /s /q"
    else:
        cmd = "rm -rf "+path
    result = getstatusoutput(cmd)
    if(result[0]!=0):
        raise RuntimeError(result[1])
ВЕЧЕРА
источник
33
-1. Весь смысл использования shutil.rmdirзаключается в том, чтобы изолировать вас от типа операционной системы.
mtrw
3
Я понимаю концепцию, но когда кто-то хорошо осознает тот факт, что он хочет полностью удалить папку, то какой смысл обходить все дерево файлов? shutil.rmdir, в частности, вызывает os.listdir (), os.path.islink () и т. д. и т. д., некоторые проверки, которые на самом деле не всегда нужны, поскольку все, что нужно, - это отсоединить узел файловой системы. Кроме некоторых сборочных систем, таких как MSWindows для разработки MSAuto / WinCE, shtuil.rmdir почти всегда завершится сбоем, так как пакетная разработка MSAuto блокирует некоторые файлы wierd сборки при неудачном завершении, и только rmdir / S / Q или перезапуск полезны для очистки их.
вечера
2
да, просто rm ближе к ядру, использует меньше времени, памяти и процессора ..... и, как я уже сказал, причина для использования этого метода была из-за блокировок, оставленных сценариями пакетной сборки MSAuto ...
PM
3
Да, но использование shutil делает код кроссплатформенным и абстрагирует детали платформы.
xshoppyx
2
Я не думаю, что за этот ответ следует проголосовать ниже 1, поскольку он представляет собой очень хороший справочник для обхода определенных ситуаций, в которых может заинтересоваться читатель. Мне нравится размещать несколько методов, ранжированных по порядку. Так что, хотя мне не нужно это использовать, теперь я знаю, что это можно сделать и как.
kmcguire
5

Просто несколько вариантов Python 3.5, чтобы завершить ответы выше. (Мне бы очень хотелось найти их здесь).

import os
import shutil
from send2trash import send2trash # (shutil delete permanently)

Удалить папку, если она пуста

root = r"C:\Users\Me\Desktop\test"   
for dir, subdirs, files in os.walk(root):   
    if subdirs == [] and files == []:
           send2trash(dir)
           print(dir, ": folder removed")

Удалить также папку, если она содержит этот файл

    elif subdirs == [] and len(files) == 1: # if contains no sub folder and only 1 file 
        if files[0]== "desktop.ini" or:  
            send2trash(dir)
            print(dir, ": folder removed")
        else:
            print(dir)

удалить папку, если она содержит только файлы .srt или .txt

    elif subdirs == []: #if dir doesn’t contains subdirectory
        ext = (".srt", ".txt")
        contains_other_ext=0
        for file in files:
            if not file.endswith(ext):  
                contains_other_ext=True
        if contains_other_ext== 0:
                send2trash(dir)
                print(dir, ": dir deleted")

Удалить папку, если ее размер меньше 400 КБ:

def get_tree_size(path):
    """Return total size of files in given path and subdirs."""
    total = 0
    for entry in os.scandir(path):
        if entry.is_dir(follow_symlinks=False):
            total += get_tree_size(entry.path)
        else:
            total += entry.stat(follow_symlinks=False).st_size
    return total


for dir, subdirs, files in os.walk(root):   
    If get_tree_size(dir) < 400000:  # ≈ 400kb
        send2trash(dir)
    print(dir, "dir deleted")
JinSnow
источник
4
Пожалуйста, исправьте отступ и кодif files[0]== "desktop.ini" or:
Mr_and_Mrs_D
5

Я хотел бы добавить «чистый путь»:

from pathlib import Path
from typing import Union

def del_dir(target: Union[Path, str], only_if_empty: bool = False):
    target = Path(target).expanduser()
    assert target.is_dir()
    for p in sorted(target.glob('**/*'), reverse=True):
        if not p.exists():
            continue
        p.chmod(0o666)
        if p.is_dir():
            p.rmdir()
        else:
            if only_if_empty:
                raise RuntimeError(f'{p.parent} is not empty!')
            p.unlink()
    target.rmdir()

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

Льготы:

  • Он НЕ полагается на внешние двоичные файлы: все использует модули с питанием от батарей Python (Python> = 3.6)
  • Это быстро и эффективно использует память: нет стека рекурсии, нет необходимости запускать подпроцесс
  • Это кроссплатформенный (по крайней мере, это то, что pathlib обещано в Python 3.6; ни одна из указанных выше операций не работает в Windows)
  • При необходимости можно вести очень детальную регистрацию, например, регистрировать каждое удаление, как оно происходит.
pepoluan
источник
Можете ли вы предоставить также пример использования, например. del_dir (Path ())? Спасибо
Икапра
@lcapra Просто вызовите его с каталогом для удаления в качестве первого аргумента.
pepoluan
3
def deleteDir(dirPath):
    deleteFiles = []
    deleteDirs = []
    for root, dirs, files in os.walk(dirPath):
        for f in files:
            deleteFiles.append(os.path.join(root, f))
        for d in dirs:
            deleteDirs.append(os.path.join(root, d))
    for f in deleteFiles:
        os.remove(f)
    for d in deleteDirs:
        os.rmdir(d)
    os.rmdir(dirPath)
amazingthere
источник
Замечательно сделать скрипт, который помещает файл в четырнадцать, а потом удаляет их вслепую.
racribeiro
3

Если вы не хотите использовать shutilмодуль, вы можете просто использовать osмодуль.

from os import listdir, rmdir, remove
for i in listdir(directoryToRemove):
    os.remove(os.path.join(directoryToRemove, i))
rmdir(directoryToRemove) # Now the directory is empty of files
Байрон Филер
источник
2
os.removeне может удалить каталоги, поэтому он будет повышен, OsErrorесли directoryToRemoveсодержит подкаталоги.
Одноименный
#pronetoraceconditions
kapad
3

Десять лет спустя и с использованием Python 3.7 и Linux все еще существуют разные способы сделать это:

import subprocess
from pathlib import Path

#using pathlib.Path
path = Path('/path/to/your/dir')
subprocess.run(["rm", "-rf", str(path)])

#using strings
path = "/path/to/your/dir"
subprocess.run(["rm", "-rf", path])

По сути, он использует модуль подпроцесса Python для запуска скрипта bash, $ rm -rf '/path/to/your/dirкак если бы вы использовали терминал для выполнения той же задачи. Это не полностью Python, но это делается.

Причина, по которой я включил этот pathlib.Pathпример, заключается в том, что, по моему опыту, он очень полезен при работе со многими путями, которые меняются. Дополнительные этапы импорта pathlib.Pathмодуля и преобразования конечных результатов в строки часто обходятся мне дешевле из-за времени разработки. Было бы удобно, если бы Path.rmdir()присутствовал параметр arg для явной обработки непустых директорий.

RodogInfinite
источник
Я также перешел на этот подход, потому что я столкнулся с проблемами rmtreeи скрытых папок, как .vscode. Эта папка была обнаружена как текстовый файл, и ошибка сказала мне, что этот файл был busyи не может быть удален.
Даниэль Эйзенрайх
2

Удалить папку, даже если она может не существовать (избегая условия гонки в ответе Чарльза Чоу ), но при этом все равно возникают ошибки, когда что-то идет не так (например, проблемы с правами доступа, ошибка чтения с диска, файл не является каталогом)

Для Python 3.x:

import shutil

def ignore_absent_file(func, path, exc_inf):
    except_instance = exc_inf[1]
    if isinstance(except_instance, FileNotFoundError):
        return
    raise except_instance

shutil.rmtree(dir_to_delete, onerror=ignore_absent_file)

Код Python 2.7 почти такой же:

import shutil
import errno

def ignore_absent_file(func, path, exc_inf):
    except_instance = exc_inf[1]
    if isinstance(except_instance, OSError) and \
        except_instance.errno == errno.ENOENT:
        return
    raise except_instance

shutil.rmtree(dir_to_delete, onerror=ignore_absent_file)
Одноименный
источник
1

С os.walk я бы предложил решение, которое состоит из 3 однострочных вызовов Python:

python -c "import sys; import os; [os.chmod(os.path.join(rs,d), 0o777) for rs,ds,fs in os.walk(_path_) for d in ds]"
python -c "import sys; import os; [os.chmod(os.path.join(rs,f), 0o777) for rs,ds,fs in os.walk(_path_) for f in fs]"
python -c "import os; import shutil; shutil.rmtree(_path_, ignore_errors=False)"

Первый скрипт chmod - все подкаталоги, второй скрипт chmod - все файлы. Затем третий сценарий удаляет все без препятствий.

Я проверил это из «Shell Script» на задании Jenkins (я не хотел сохранять новый скрипт Python в SCM, поэтому искал однострочное решение), и он работал для Linux и Windows.

Александр Самойлов
источник
С помощью pathlib, вы можете объединить первые два шага в один:[p.chmod(0o666) for p in pathlib.Path(_path_).glob("**/*")]
pepoluan
0

Вы можете использовать команду os.system для простоты:

import os
os.system("rm -rf dirname")

Как очевидно, он фактически вызывает системный терминал для выполнения этой задачи.


источник
19
Извините, это Unpythonic и зависит от платформы.
Ами Тавори
0

Я нашел очень простой способ удалить любую папку (даже НЕ пустую) или файл в ОС WINDOWS .

os.system('powershell.exe  rmdir -r D:\workspace\Branches\*%s* -Force' %CANDIDATE_BRANCH)
Шеремет
источник
0

Для Windows, если каталог не пустой, и у вас есть файлы только для чтения, или вы получаете ошибки, такие как

  • Access is denied
  • The process cannot access the file because it is being used by another process

Попробуй это, os.system('rmdir /S /Q "{}"'.format(directory))

Это эквивалентно для rm -rfLinux / Mac.

Картик Радж
источник