Comment supprimer / supprimer un dossier qui n'est pas vide?

847

J'obtiens une erreur «accès refusé» lorsque j'essaie de supprimer un dossier qui n'est pas vide. J'ai utilisé la commande suivante dans ma tentative: os.remove("/folder_name").

Quelle est la manière la plus efficace de supprimer / supprimer un dossier / répertoire qui n'est pas vide?

Amara
la source
32
Notez également que même si le répertoire était vide, os.remove échouerait à nouveau, car la fonction correcte est os.rmdir.
tzot
Et pour un rm -rfcomportement spécifique , voir: stackoverflow.com/questions/814167/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

1349
import shutil

shutil.rmtree('/folder_name')

Référence de bibliothèque standard: shutil.rmtree .

De par sa conception, rmtreeéchoue sur les arborescences de dossiers contenant des fichiers en lecture seule. Si vous souhaitez que le dossier soit supprimé, qu'il contienne ou non des fichiers en lecture seule, utilisez

shutil.rmtree('/folder_name', ignore_errors=True)
ddaa
la source
73
Notez que rmtreecela échouera s'il y a des fichiers en lecture seule: stackoverflow.com/questions/2656322/…
Sridhar Ratnakumar
9
Cela ne fonctionne pas pour moi: Traceback (dernier appel le plus récent): Fichier "foo.py", ligne 31, dans <module> shutil.rmtree (thistestdir) Fichier "/usr/lib/python2.6/shutil.py ", ligne 225, dans rmtree onerror (os.rmdir, chemin, sys.exc_info ()) Fichier" /usr/lib/python2.6/shutil.py ", ligne 223, dans rmtree os.rmdir (chemin) OSError: [Errno 90] Répertoire non vide: '/ path / to / rmtree'
Clayton Hughes
4
Clayton: selon toute vraisemblance, un fichier a été ajouté simultanément pendant que rmtree était occupé à supprimer des éléments, "rm -rf" échouerait de la même façon.
ddaa
14
Quelqu'un sait pourquoi cette fonctionnalité n'est pas dans le package os? On dirait que os.rmdir est tout à fait inutile. De bons arguments pour expliquer pourquoi il est mis en œuvre de cette façon?
Malcolm
21
@Malcolm Le package est un wrapper pour les fonctions du système d'exploitation. Sur les systèmes POSIX , rmdir échouera si le répertoire n'est pas vide. Ubuntu et Windows sont des exemples populaires de conformité POSIX à cet égard.
Iain Samuel McLean Elder
138

De la documentation python sur 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
la source
1
Eh bien, je me trompe peut-être de downmodding. Mais je peux, maintenant je pense que c'est vrai.
ddaa
3
@ddaa: Bien que l'utilisation de shutil soit certainement le moyen le plus simple, il n'y a certainement rien d'antipythonique dans cette solution. Je n'aurais pas voté pour cette réponse, mais je n'ai cette fois que pour annuler votre downvote :)
Jeremy Cantrell
7
Le code lui-même est pythonique. L'utiliser à la place de shutil.rmtree dans un vrai programme serait contraire à la loi: ce serait ignorer "une façon évidente de le faire". Quoi qu'il en soit, c'est de la sémantique, en supprimant le downmod.
ddaa
2
@ddaa Est-il contraire à la logique de vouloir enregistrer chaque fichier ou répertoire supprimé? Je ne sais pas comment faire cela avec shutil.rmtree?
Jonathan Komar
4
@ddaa C'était matière à réflexion, c'est-à-dire à la rhétorique. Je sais ce que je fais. J'ai juste pensé que vous aimeriez peut-être reconsidérer "la manière évidente de le faire" en fournissant une raison pour laquelle shutil.rmtree peut ne pas être le bon "ajustement".
Jonathan Komar
112
import shutil
shutil.rmtree(dest, ignore_errors=True)
Siva Mandadi
la source
1
Ceci est la bonne réponse. Dans mon système, même si je mets tout dans le dossier en lecture-écriture, j'obtiens une erreur lorsque j'essaie de le supprimer. ignore_errors=Truerésout le problème.
Aventinus
3
Dans ma réponse, le onerrorparamètre est utilisé à la place de ignore_errors. De cette façon, les fichiers en lecture seule sont supprimés plutôt qu'ignorés.
Dave Chandler
Oui, cela ne supprimera pas les fichiers en cas d'erreur. Donc, fondamentalement, la rmtree()méthode entière est ignorée.
Juha Untinen
2
Cela aurait dû être une petite modification de la réponse acceptée 6 ans plus tôt, plutôt une nouvelle réponse. Je vais le faire maintenant.
Jean-François Corbett
22

à partir de python 3.4, vous pouvez utiliser:

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

pthest une pathlib.Pathinstance. Bien, mais peut-être pas le plus rapide.

yota
la source
10

De docs.python.org :

Cet exemple montre comment supprimer une arborescence de répertoires sous Windows où certains fichiers ont leur bit de lecture seule défini. Il utilise le rappel onerror pour effacer le bit en lecture seule et réessayer la suppression. Tout échec ultérieur se propagera.

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
la source
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) 

Si ignore_errors est défini, les erreurs sont ignorées; sinon, si onerror est défini, il est appelé pour gérer l'erreur avec des arguments (func, path, exc_info) où func est os.listdir, os.remove ou os.rmdir; chemin est l'argument de cette fonction qui a provoqué son échec; et exc_info est un tuple renvoyé par sys.exc_info (). Si ignore_errors est false et onerror est None, une exception est déclenchée. Entrez le code ici

RY Zheng
la source
Selon les documents , les exceptions soulevées par onerror ne seront pas détectées, donc je ne suis pas sûr que votre code de saisie augmente signifie quelque chose.
kmarsh du
-1. Cela semble trop compliqué par rapport à la réponse de Dave Chandler. De plus, si nous voulons supprimer en lecture seule, nous n'avons pas besoin de rendre les fichiers exécutables.
idbrii
7

Basé sur la réponse de kkubasik, vérifiez si le dossier existe avant de le supprimer, plus robuste

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
la source
6
cela introduit une condition de course possible
Corey Goldberg
1
selon la façon la plus pythonique de supprimer un fichier qui peut ne pas exister , il est préférable de trysupprimer et de gérer exceptque d'appeler en exists()premier
TT
6

si vous êtes sûr que vous voulez supprimer tout l'arborescence de dir et que vous n'êtes plus intéressé par le contenu de dir, alors l'exploration de l'arborescence de dir entière est stupide ... il suffit d'appeler la commande native OS de python pour le faire. Il sera plus rapide, efficace et consommera moins de mémoire.

RMDIR c:\blah /s /q 

ou * nix

rm -rf /home/whatever 

En python, le code ressemblera à ..

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])
PM
la source
33
-1. L'intérêt de l'utilisation shutil.rmdirest de vous isoler du type de système d'exploitation.
mtrw
3
Je comprends le concept, mais quand on sait très bien qu'il souhaite supprimer complètement le dossier, quel est l'intérêt d'explorer l'arborescence de fichiers entière? shutil.rmdir appelle spécifiquement os.listdir (), os.path.islink () etc etc. quelques vérifications qui ne sont pas vraiment toujours nécessaires, car il suffit de dissocier le nœud du système de fichiers. À côté de certains systèmes de construction, comme MSWindows pour le développement MSAuto / WinCE, shtuil.rmdir échouera presque toujours, car le développement par lots MSAuto verrouille certains fichiers de construction bizarres en cas d'échec de la sortie, et seuls rmdir / S / Q ou redémarrage sont utiles pour nettoyer leur.
PM
2
oui, juste rm est plus proche du noyau, en utilisant moins de temps, de mémoire et de processeur ..... et comme je l'ai dit, la raison pour laquelle j'ai utilisé cette méthode était à cause des verrous laissés par les scripts de construction par lots de MSAuto ...
PM
3
Oui, mais l'utilisation de shutil rend le code multiplateforme et résume les détails de la plate-forme.
xshoppyx
2
Je ne pense pas que cette réponse devrait être rejetée en dessous de 1 car elle fournit une très bonne référence pour un travail autour de certaines situations dans lesquelles un lecteur pourrait être intéressé. J'aime avoir plusieurs méthodes affichées avec elles classées dans l'ordre. Donc, même si je n'ai pas besoin de l'utiliser, je sais maintenant que cela peut être fait et comment.
kmcguire
5

Juste quelques options de python 3.5 pour compléter les réponses ci-dessus. (J'aurais adoré les trouver ici).

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

Supprimer le dossier s'il est vide

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

Supprimer également le dossier s'il contient ce fichier

    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)

supprimer le dossier s'il ne contient que des fichiers .srt ou .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")

Supprimer le dossier si sa taille est inférieure à 400 Ko:

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
la source
4
Veuillez corriger l'indentation et le codeif files[0]== "desktop.ini" or:
Mr_and_Mrs_D
5

Je voudrais ajouter une approche "pure pathlib":

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

Cela repose sur le fait qu'il Pathest ordonnable, et les chemins plus longs seront toujours triés après les chemins plus courts, tout comme str. Par conséquent, les répertoires viendront avant les fichiers. Si nous inversons le tri, les fichiers arriveront avant leurs conteneurs respectifs, nous pouvons donc simplement les dissocier / rmdir un par un en un seul passage.

Avantages:

  • Il ne s'appuie PAS sur des binaires externes: tout utilise les modules inclus dans les batteries de Python (Python> = 3.6)
  • C'est rapide et efficace en mémoire: pas de pile de récursivité, pas besoin de démarrer un sous-processus
  • C'est multiplateforme (du moins, c'est ce qui pathlibpromet dans Python 3.6; aucune opération ci-dessus n'a déclaré ne pas fonctionner sur Windows)
  • Si nécessaire, on peut effectuer une journalisation très granulaire, par exemple, enregistrer chaque suppression au fur et à mesure.
pepoluan
la source
pouvez-vous également fournir un exemple d'utilisation, par exemple. del_dir (Chemin ())? Merci
lcapra
@lcapra Appelez-le simplement avec le répertoire à supprimer comme premier 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)
étonnant
la source
Idéal pour créer un script qui met le fichier en quarantaine avant de le supprimer aveuglément.
racribeiro
3

Si vous ne souhaitez pas utiliser le shutilmodule, vous pouvez simplement utiliser le osmodule.

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
la source
2
os.removene peut pas supprimer les répertoires, ce qui augmentera OsErrorsi directoryToRemovecontient des sous-répertoires.
Éponyme
#pronetoraceconditions
kapad
3

Dix ans plus tard et en utilisant Python 3.7 et Linux, il existe encore différentes façons de procéder:

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

Il s'agit essentiellement d'utiliser le module de sous-processus de Python pour exécuter le script bash $ rm -rf '/path/to/your/dircomme si vous utilisiez le terminal pour accomplir la même tâche. Ce n'est pas entièrement en Python, mais cela se fait.

La raison pour laquelle j'ai inclus l' pathlib.Pathexemple est que, selon mon expérience, il est très utile lorsqu'il s'agit de nombreux chemins qui changent. Les étapes supplémentaires d'importation du pathlib.Pathmodule et de conversion des résultats finaux en chaînes sont souvent moins coûteuses pour moi en termes de temps de développement. Il serait pratique de disposer Path.rmdir()d'une option arg pour gérer explicitement les répertoires non vides.

RodogInfinite
la source
Je suis également passé à cette approche, car j'ai rencontré des problèmes avec rmtreedes dossiers cachés comme .vscode. Ce dossier a été détecté en tant que fichier texte et l'erreur m'a dit que ce fichier était busyet ne pouvait pas être supprimé.
Daniel Eisenreich
2

Pour supprimer un dossier même s'il n'existe peut-être pas (en évitant la condition de concurrence critique dans la réponse de Charles Chow ) mais qu'il y a toujours des erreurs lorsque d'autres choses vont mal (par exemple, des problèmes d'autorisation, une erreur de lecture sur le disque, le fichier n'est pas un répertoire)

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

Le code Python 2.7 est presque le même:

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)
Éponyme
la source
1

Avec os.walk, je proposerais la solution qui consiste en 3 appels Python à une ligne:

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

Tous les sous-répertoires du premier script chmod, tous les fichiers du second script chmod. Ensuite, le troisième script supprime tout sans aucun obstacle.

J'ai testé cela à partir du "Shell Script" dans un travail Jenkins (je ne voulais pas stocker un nouveau script Python dans SCM, c'est pourquoi j'ai cherché une solution sur une ligne) et cela a fonctionné pour Linux et Windows.

Alexander Samoylov
la source
Avec pathlib, vous pouvez combiner les deux premières étapes en une seule:[p.chmod(0o666) for p in pathlib.Path(_path_).glob("**/*")]
pepoluan
0

Vous pouvez utiliser la commande os.system pour plus de simplicité:

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

Comme il est évident, il appelle en fait le terminal système pour accomplir cette tâche.


la source
19
Désolé, ceci est Unpythonic et dépend de la plateforme.
Ami Tavory
0

J'ai trouvé un moyen très simple de supprimer n'importe quel dossier (même NON vide) ou fichier sur WINDOWS OS .

os.system('powershell.exe  rmdir -r D:\workspace\Branches\*%s* -Force' %CANDIDATE_BRANCH)
seremet
la source
0

Pour Windows, si le répertoire n'est pas vide et que vous avez des fichiers en lecture seule ou des erreurs comme

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

Essaye ça, os.system('rmdir /S /Q "{}"'.format(directory))

C'est équivalent rm -rfsous Linux / Mac.

Kartik Raj
la source