Як видалити / видалити папку, яка не порожня?


846

Я отримую помилку "доступ заборонено", коли я намагаюся видалити папку, яка не порожня. Я використовував таку команду в мою спробу: os.remove("/folder_name").

Який найефективніший спосіб видалення / видалення папки / каталогу, які не є порожніми?


32
Також зауважте, що навіть якщо каталог був порожнім, os.remove знову не вдасться, оскільки правильною функцією є os.rmdir.
tzot

А для конкретного rm -rfповедінки см: stackoverflow.com/questions/814167 / ...
Чіро Сантіллі冠状病毒审查六四事件法轮功

Відповіді:


1345
import shutil

shutil.rmtree('/folder_name')

Стандартна довідка бібліотеки: shutil.rmtree .

За задумом, rmtreeпомилки на деревах папок, що містять файли лише для читання. Якщо ви хочете видалити папку незалежно від того, чи вона містить файли лише для читання, скористайтеся

shutil.rmtree('/folder_name', ignore_errors=True)

73
Зверніть увагу , що rmtreeзазнає невдачі , якщо є тільки для читання файлів: stackoverflow.com/questions/2656322 / ...
Шрідхара Ratnakumar

9
Це не працює для мене: Traceback (останній дзвінок останній): Файл "foo.py", рядок 31, у <module> shutil.rmtree (thistestdir) Файл "/usr/lib/python2.6/shutil.py ", рядок 225, в rmtree onerror (os.rmdir, path, sys.exc_info ()) Файл" /usr/lib/python2.6/shutil.py ", рядок 223, у rmtree os.rmdir (path) OSError: [Errno 90] Каталог не порожній: '/ path / to / rmtree "
Clayton Hughes

4
Клейтон: швидше за все, файл доданий одночасно, тоді як rmtree був зайнятий видаленням матеріалів, "rm -rf" не вдасться до того ж.
ddaa

13
Хтось знає, чому ця функціональність відсутня в пакеті OS? Здається, що os.rmdir досить марний. Будь-які хороші аргументи, чому це реалізовано саме так?
Малькольм

21
@Malcolm Пакет являє собою обгортку для функцій ОС. У системах POSIX rmdir вийде з ладу, якщо каталог не порожній. Ubuntu та Windows - популярні приклади відповідності POSIX у цьому відношенні.
Ієн Самуель Маклін Старший

138

З документів python на 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))

1
Ну, можливо, я помиляюся з недомовленістю. Але я можу, зараз я думаю, що це правильно.
ddaa

3
@ddaa: Хоча використання shutil, безумовно, найпростіший спосіб, у цьому рішенні, звичайно, немає нічого непітонічного. Я б не підтримав цю відповідь, але у мене є час лише відмовитись від вашого голосування :)
Джеремі Кантрелл

7
Сам код - пітонічний. Використовувати його замість shutil.rmtree в реальній програмі було б непітонічно: це ігнорувало б "один очевидний спосіб зробити це". Так чи інакше, це семантика, видалення надолу.
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()метод ігнорується.
Juha Untinen

1
Це мала бути невелика редакція відповіді, прийнятої на 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, щоб очистити біт для читання та повторити спробу видалити. Будь-який наступний збій пошириться.

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; path - це аргумент до тієї функції, яка спричинила її збій; і exc_info - кортеж, повернутий sys.exc_info (). Якщо ignore_errors невірно, а onerror - None, тут піднімається виняток


Згідно з документами , винятки, підняті onerror, не потраплять, тому я не впевнений, що ваш код введення підвищення тут нічого не означає.
кмарш

-1. Це здається надскладним порівняно з відповіддю Дейва Чендлера. Крім того, якщо ми хочемо видалити лише заново, нам не потрібно робити файли виконуваними.
idbrii

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 або перезапуск. їх.
PM

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")

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)
  • Якщо потрібно, можна зробити дуже деталізований журнал, наприклад, журнал кожного видалення, як це відбувається.

Ви можете навести також приклад використання, наприклад, del_dir (Шлях ())? Дякую
lcapra

@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)

Чудово робити сценарій, який ставить файл у чотиринадцять, перш ніж їх наосліп видалити
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()прийшов варіант аргументу, щоб явно обробляти не порожні dir.


Я також перейшов на такий підхід, тому що я зіткнувся з проблемами із 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. Тоді третій сценарій видаляє все без перешкод.

Я перевірив це з "Сценарію оболонки" в роботі Дженкінса (я не хотів зберігати новий скрипт 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
Вибачте, це непітонічно і залежно від платформи.
Амі Таворі

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 -rfв Linux / Mac.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.