Jak usunąć / usunąć folder, który nie jest pusty?

846

Podczas próby usunięcia folderu, który nie jest pusty, pojawia się błąd „odmowa dostępu”. Użyłem następującego polecenia w moim próba: os.remove("/folder_name").

Jaki jest najskuteczniejszy sposób usuwania / usuwania folderu / katalogu, który nie jest pusty?

Amara
źródło
32
Zauważ też, że nawet jeśli katalog byłby pusty, os.remove ponownie by się nie powiodło, ponieważ poprawną funkcją jest os.rmdir.
tzot
rm -rf
Aby poznać

Odpowiedzi:

1345
import shutil

shutil.rmtree('/folder_name')

Odwołanie do biblioteki standardowej: shutil.rmtree .

Z założenia rmtreezawodzi w drzewach folderów zawierających pliki tylko do odczytu. Jeśli chcesz usunąć folder bez względu na to, czy zawiera on pliki tylko do odczytu, użyj

shutil.rmtree('/folder_name', ignore_errors=True)
ddaa
źródło
73
Pamiętaj, że rmtreezakończy się niepowodzeniem, jeśli istnieją pliki tylko do odczytu: stackoverflow.com/questions/2656322/…
Sridhar Ratnakumar
9
To nie działa dla mnie: Traceback (ostatnie ostatnie połączenie): Plik „foo.py”, wiersz 31, w pliku <module> shutil.rmtree (thistestdir) ”/usr/lib/python2.6/shutil.py ", wiersz 225, w rmtree onerror (os.rmdir, ścieżka, plik sys.exc_info ())" /usr/lib/python2.6/shutil.py ", wiersz 223, w rmtree os.rmdir (ścieżka) OSError: [Errno 90] Katalog nie jest pusty: „/ path / to / rmtree”
Clayton Hughes
4
Clayton: najprawdopodobniej plik został dodany jednocześnie, podczas gdy rmtree był zajęty usuwaniem rzeczy, „rm -rf” nie powiedzie się tak samo.
ddaa
13
Czy ktoś wie, dlaczego tej funkcji nie ma w pakiecie OS? Wydaje się, że os.rmdir jest całkiem bezużyteczny. Jakieś dobre argumenty przemawiające za tym, dlaczego został on wdrożony w ten sposób?
Malcolm
21
@Malcolm Pakiet jest opakowaniem dla funkcji systemu operacyjnego. W systemach POSIX rmdir nie powiedzie się, jeśli katalog nie jest pusty. Pod tym względem Ubuntu i Windows są popularnymi przykładami zgodności z POSIX.
Iain Samuel McLean Elder
138

Od docs Pythona na 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
źródło
1
Cóż, może mylę się w kwestii downmodingu. Ale mogę, teraz myślę, że to prawda.
ddaa,
3
@ddaa: Podczas gdy korzystanie z shutil jest zdecydowanie najłatwiejszym sposobem, z pewnością nie ma w tym rozwiązaniu niczego nieokreślonego. Nie głosowałbym za odpowiedzią w tej sprawie, ale mam ten czas tylko na anulowanie twojego komentarza :)
Jeremy Cantrell,
7
Sam kod jest pythoniczny. Używanie go zamiast shutil.rmtree w prawdziwym programie byłoby niepythonic: byłoby to ignorowanie „jednego oczywistego sposobu na zrobienie tego”. W każdym razie jest to semantyka, usuwająca downmod.
ddaa,
2
@ddaa Czy logiczne jest, aby logować każdy usunięty plik lub katalog? Nie jestem pewien, jak to zrobić za pomocą shutil.rmtree?
Jonathan Komar
4
@ddaa To było jedzenie dla myśli, czyli retoryki. Wiem co robię. Pomyślałem, że może zechcesz ponownie rozważyć „oczywisty sposób na zrobienie tego”, podając powód, dla którego shutil.rmtree może nie być odpowiednim „dopasowaniem”.
Jonathan Komar
112
import shutil
shutil.rmtree(dest, ignore_errors=True)
Siva Mandadi
źródło
1
To jest poprawna odpowiedź. W moim systemie, mimo że wszystko w danym folderze ustawiam na zapis i odczyt, pojawia się błąd przy próbie usunięcia. ignore_errors=Truerozwiązuje problem.
Aventinus,
3
W mojej odpowiedzi onerrorparametr jest używany zamiast ignore_errors. W ten sposób pliki tylko do odczytu są usuwane, a nie ignorowane.
Dave Chandler
Tak, nie spowoduje to usunięcia plików po błędzie. Zasadniczo cała rmtree()metoda jest ignorowana.
Juha Untinen,
1
To powinna być niewielka zmiana odpowiedzi zaakceptowanej 6 lat wcześniej, a raczej nowa odpowiedź. Zrobię to teraz.
Jean-François Corbett
22

z Pythona 3.4 możesz użyć:

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

gdzie pthjest pathlib.Pathinstancja. Fajnie, ale może nie być najszybszy.

yota
źródło
10

Z docs.python.org :

W tym przykładzie pokazano, jak usunąć drzewo katalogów w systemie Windows, w którym niektóre pliki mają ustawiony bit tylko do odczytu. Używa wywołania zwrotnego onerror, aby wyczyścić bit tylko do odczytu i ponowić próbę usunięcia. Wszelkie kolejne niepowodzenia będą się rozprzestrzeniać.

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)
Dave Chandler
źródło
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) 

Jeśli ustawione jest ignore_errors, błędy są ignorowane; w przeciwnym razie, jeśli ustawiony jest onerror, wywoływany jest do obsługi błędu z argumentami (func, path, exc_info), gdzie func to os.listdir, os.remove lub os.rmdir; ścieżka jest argumentem tej funkcji, która spowodowała jej awarię; a exc_info to krotka zwrócona przez sys.exc_info (). Jeśli ignore_errors ma wartość false, a onerror ma wartość None, zgłaszany jest wyjątek. Wprowadź kod tutaj

RY Zheng
źródło
Zgodnie z docs , wyjątki podniesione przez onError nie zostanie złapany , więc nie jestem pewien, Twój podbicie wpisać kod tutaj coś znaczy.
kmarsh
-1. To wydaje się zbyt skomplikowane w porównaniu z odpowiedzią Dave'a Chandlera. Ponadto, jeśli chcemy usunąć tylko do odczytu, nie musimy tworzyć plików wykonywalnych.
idbrii
7

Oprzyj się na odpowiedzi kkubasika, sprawdź, czy folder istnieje przed usunięciem, bardziej solidny

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")
Charles Chow
źródło
6
wprowadza to możliwy stan wyścigu
Corey Goldberg
1
zgodnie z najbardziej pythonicznym sposobem usuwania pliku, który może nie istnieć , lepiej jest tryusunąć i obsłużyć exceptniż exists()najpierw zadzwonić
TT--
6

jeśli jesteś pewien, że chcesz usunąć całe drzewo katalogów i nie jesteś już zainteresowany zawartością katalogu, to przeszukiwanie całego drzewa katalogów jest głupotą ... po prostu wywołaj natywną komendę systemu operacyjnego z python, aby to zrobić. Będzie szybszy, wydajniejszy i zużywa mniej pamięci.

RMDIR c:\blah /s /q 

lub * nix

rm -rf /home/whatever 

W pythonie kod będzie wyglądał jak ...

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])
PO POŁUDNIU
źródło
33
-1. Chodzi przede wszystkim o shutil.rmdirto, aby odizolować Cię od rodzaju systemu operacyjnego.
mtrw
3
Rozumiem tę koncepcję, ale jeśli ktoś jest świadomy faktu, że chce całkowicie usunąć folder, to po co przeszukiwać całe drzewo plików? shutil.rmdir w szczególności wywołuje os.listdir (), os.path.islink () itp. itd. niektóre kontrole, które nie zawsze są potrzebne, ponieważ wszystko, czego potrzeba, to rozłączenie węzła systemu plików. Oprócz niektórych systemów kompilacji, takich jak MSWindows do programowania MSAuto / WinCE, wtedy shtuil.rmdir prawie zawsze kończy się niepowodzeniem, ponieważ programowanie wsadowe MSAuto blokuje niektóre dziwne pliki kompilacji przy nieudanym wyjściu, a tylko rmdir / S / Q lub restart jest pomocny do czyszczenia im.
PM,
2
tak, tylko rm jest bliżej jądra, zużywa mniej czasu, pamięci i procesora ..... i jak powiedziałem, powodem dla którego użyłem tej metody były blokady pozostawione przez skrypty budowania partii MSAuto ...
PM
3
Tak, ale użycie shutil sprawia, że ​​kod jest wieloplatformowy i oddziela szczegóły platformy.
xshoppyx,
2
Nie sądzę, aby odpowiedź ta została odrzucona poniżej 1, ponieważ stanowi bardzo miłe odniesienie do obejrzenia pewnych sytuacji, w których czytelnik może być zainteresowany. Lubię publikowanie wielu metod w kolejności. Więc nawet jeśli nie muszę tego używać, wiem, że można to zrobić i jak.
kmcguire
5

Tylko niektóre opcje Pythona do uzupełnienia powyższych odpowiedzi. (Chciałbym je tutaj znaleźć).

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

Usuń folder, jeśli jest pusty

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

Usuń także folder, jeśli zawiera ten plik

    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)

usuń folder, jeśli zawiera tylko pliki .srt lub .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")

Usuń folder, jeśli jego rozmiar jest mniejszy niż 400 KB:

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
źródło
4
Popraw wcięcie i kodif files[0]== "desktop.ini" or:
Mr_and_Mrs_D
5

Chciałbym dodać podejście „czystej ścieżki”:

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

Zależy to od tego, że Pathjest uporządkowane, a dłuższe ścieżki zawsze będą sortować po krótszych ścieżkach, tak jak str. Dlatego katalogi pojawią się przed plikami. Jeśli się odwrócimy sortowanie, pliki pojawią się przed ich odpowiednimi kontenerami, więc możemy je po prostu odłączyć / rmdir jeden po drugim za jednym przejściem.

Korzyści:

  • NIE jest oparty na zewnętrznych plikach binarnych: wszystko korzysta z modułów zawartych w bateriach Pythona (Python> = 3.6)
  • Jest szybki i energooszczędny: bez stosu rekurencyjnego, bez potrzeby uruchamiania podprocesu
  • Jest wieloplatformowy (przynajmniej tyle pathlib obiecuje w Pythonie 3.6; żadna z powyższych operacji nie działa w systemie Windows)
  • W razie potrzeby można wykonać bardzo szczegółowe rejestrowanie, np. Rejestrować każde usunięcie w miarę upływu czasu.
pepoluan
źródło
czy możesz podać również przykład użycia, np. del_dir (Path ())? Dzięki
lcapra,
@lcapra Wystarczy wywołać go z katalogiem, aby usunąć jako pierwszy argument.
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)
niesamowicie
źródło
Wspaniale jest stworzyć skrypt, który umieszcza plik w kwarantannie przed usunięciem go na ślepo.
racribeiro
3

Jeśli nie chcesz korzystać z shutilmodułu, możesz po prostu użyć osmodułu.

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
Byron Filer
źródło
2
os.removenie można usunąć katalogów, więc spowoduje to wzrost, OsErrorjeśli directoryToRemovezawiera podkatalogi.
Tytułowy
#pronetoraceconditions
kapad
3

Dziesięć lat później i przy użyciu Pythona 3.7 i Linuksa istnieją jeszcze różne sposoby:

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

Zasadniczo używa modułu podprocesu Pythona do uruchamiania skryptu bash, $ rm -rf '/path/to/your/dirtak jakbyś używał terminala do wykonania tego samego zadania. To nie jest w pełni Python, ale robi to.

Podałem pathlib.Pathprzykład, ponieważ z mojego doświadczenia wynika, że ​​jest bardzo przydatny, gdy mamy do czynienia z wieloma zmieniającymi się ścieżkami. Dodatkowe etapy importowania pathlib.Pathmodułu i konwertowania wyników końcowych na ciągi są często dla mnie niższym kosztem czasu programowania. Byłoby wygodne, gdyby Path.rmdir()był wyposażony w opcję arg do jawnej obsługi niepustych katalogów.

RodogInfinite
źródło
Przełączyłem się również na to podejście, ponieważ napotkałem problemy z rmtreeukrytymi folderami takimi jak .vscode. Ten folder został wykryty jako plik tekstowy, a błąd powiedział mi, że ten plik został busyi nie można go usunąć.
Daniel Eisenreich,
2

Aby usunąć folder, nawet jeśli nie istnieje (unikając warunków wyścigu w odpowiedzi Charlesa Chowa ), ale nadal występują błędy, gdy inne rzeczy idą źle (np. Problemy z uprawnieniami, błąd odczytu dysku, plik nie jest katalogiem)

W przypadku 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)

Kod Python 2.7 jest prawie taki sam:

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)
Tytułowy
źródło
1

W przypadku os.walk zaproponowałbym rozwiązanie składające się z 3 jedno-liniowych wywołań Pythona:

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

Pierwszy skrypt chmod ma wszystkie podkatalogi, drugi skrypt chmod ma wszystkie pliki. Następnie trzeci skrypt usuwa wszystko bez przeszkód.

Przetestowałem to z poziomu „Skryptu powłoki” w zadaniu Jenkinsa (nie chciałem przechowywać nowego skryptu Python w SCM, dlatego szukałem rozwiązania jednowierszowego) i działało to w systemach Linux i Windows.

Alexander Samoylov
źródło
Za pomocą pathlibmożesz połączyć dwa pierwsze kroki w jeden:[p.chmod(0o666) for p in pathlib.Path(_path_).glob("**/*")]
pepoluan
0

Dla uproszczenia możesz użyć polecenia os.system:

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

Jak oczywiste, faktycznie wykonuje terminal systemowy, aby wykonać to zadanie.


źródło
19
Przepraszamy, jest to niepythonic i zależne od platformy.
Ami Tavory
0

Znalazłem bardzo łatwy sposób na usunięcie dowolnego folderu (nawet NIE pustego) lub pliku w systemie operacyjnym WINDOWS .

os.system('powershell.exe  rmdir -r D:\workspace\Branches\*%s* -Force' %CANDIDATE_BRANCH)
seremet
źródło
0

W systemie Windows, jeśli katalog nie jest pusty i masz pliki tylko do odczytu lub występują błędy takie jak

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

Spróbuj tego, os.system('rmdir /S /Q "{}"'.format(directory))

Jest to odpowiednik rm -rfw systemach Linux / Mac.

Kartik Raj
źródło