¿Cómo elimino / elimino una carpeta que no está vacía?

847

Recibo un error de "acceso denegado" cuando intento eliminar una carpeta que no está vacía. He utilizado el comando siguiente en mi intento: os.remove("/folder_name").

¿Cuál es la forma más efectiva de eliminar / eliminar una carpeta / directorio que no está vacío?

Amara
fuente
32
También tenga en cuenta que incluso si el directorio estuviera vacío, os.remove volvería a fallar, porque la función correcta es os.rmdir.
tzot
Y para un rm -rfcomportamiento específico , ver: stackoverflow.com/questions/814167/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

1349
import shutil

shutil.rmtree('/folder_name')

Referencia de biblioteca estándar: shutil.rmtree .

Por diseño, rmtreefalla en los árboles de carpetas que contienen archivos de solo lectura. Si desea que la carpeta se elimine independientemente de si contiene archivos de solo lectura, use

shutil.rmtree('/folder_name', ignore_errors=True)
ddaa
fuente
73
Tenga en cuenta que rmtreefallará si hay archivos de solo lectura: stackoverflow.com/questions/2656322/…
Sridhar Ratnakumar
99
Esto no funciona para mí: Traceback (última llamada más reciente): Archivo "foo.py", línea 31, en <module> shutil.rmtree (thistestdir) Archivo "/usr/lib/python2.6/shutil.py ", línea 225, en rmtree onerror (os.rmdir, ruta, sys.exc_info ()) Archivo" /usr/lib/python2.6/shutil.py ", línea 223, en rmtree os.rmdir (ruta) OSError: [Errno 90] Directorio no vacío: '/ ruta / a / rmtree'
Clayton Hughes
44
Clayton: con toda probabilidad, se agregó un archivo simultáneamente mientras rmtree estaba ocupado eliminando cosas, "rm -rf" no funcionaría igual.
ddaa
14
¿Alguien sabe por qué esta funcionalidad no está en el paquete del sistema operativo? Parece que os.rmdir es bastante inútil. ¿Algún buen argumento de por qué se implementa de esta manera?
Malcolm
21
@Malcolm El paquete es un contenedor para las funciones del sistema operativo. En los sistemas POSIX , rmdir fallará si el directorio no está vacío. Ubuntu y Windows son ejemplos populares de cumplimiento POSIX a este respecto.
Iain Samuel McLean Élder
138

De los documentos de Python en 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
fuente
1
Bueno, tal vez me equivoque de downmodding. Pero puedo, ahora mismo creo que es correcto.
ddaa
3
@ddaa: Si bien el uso de shutil es definitivamente la forma más fácil, ciertamente no hay nada antipático en esta solución. No hubiera votado esta respuesta, pero solo tengo tiempo para cancelar tu voto negativo :)
Jeremy Cantrell
77
El código en sí es pitónico. Usarlo en lugar de shutil.rmtree en un programa real no sería nada: sería ignorar la "única forma obvia de hacerlo". De todos modos, esto es semántica, eliminando el downmod.
ddaa
2
@ddaa ¿No es lógico querer registrar cada archivo o directorio que se elimina? No estoy seguro de cómo hacer eso con shutil.rmtree?
Jonathan Komar
44
@ddaa Fue motivo de reflexión, es decir, retórica. Sé lo que estoy haciendo. Simplemente pensé que le gustaría reconsiderar "la forma obvia de hacerlo" al proporcionar una razón por la cual shutil.rmtree puede no ser el "ajuste" correcto.
Jonathan Komar
112
import shutil
shutil.rmtree(dest, ignore_errors=True)
Siva Mandadi
fuente
1
Esta es la respuesta correcta. En mi sistema, aunque configuro todo en la carpeta en particular para escribir-leer, aparece un error cuando intento eliminarlo. ignore_errors=Trueresuelve el problema
Aventinus
3
En mi respuesta, el onerrorparámetro se usa en lugar de ignore_errors. De esta forma, los archivos de solo lectura se eliminan en lugar de ignorarse.
Dave Chandler
Sí, esto no eliminará archivos por error. Así que básicamente rmtree()se ignora todo el método.
Juha Untinen
2
Esta debería haber sido una pequeña edición de la respuesta aceptada 6 años antes, más bien una nueva respuesta. Haré esto ahora.
Jean-François Corbett
22

desde python 3.4 puedes usar:

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

donde pthes una pathlib.Pathinstancia Agradable, pero puede no ser el más rápido.

Yota
fuente
10

Desde docs.python.org :

Este ejemplo muestra cómo eliminar un árbol de directorios en Windows donde algunos de los archivos tienen su bit de solo lectura establecido. Utiliza la devolución de llamada onerror para borrar el bit de solo lectura y volver a intentar eliminar. Cualquier falla posterior se propagará.

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
fuente
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 se establece ignore_errors, los errores se ignoran; de lo contrario, si se establece onerror, se llama para manejar el error con argumentos (func, path, exc_info) donde func es os.listdir, os.remove u os.rmdir; ruta es el argumento de esa función que causó su falla; y exc_info es una tupla devuelta por sys.exc_info (). Si ignore_errors es falso y onerror es None, se genera una excepción. Introduzca el código aquí

RY Zheng
fuente
De acuerdo con los documentos , las excepciones generadas por onerror no se detectarán, por lo que no estoy seguro de que su código de ingreso aumente aquí significa algo.
kmarsh
-1. Esto parece demasiado complicado en comparación con la respuesta de Dave Chandler. Además, si queremos eliminar solo lectura, no necesitamos hacer que los archivos sean ejecutables.
idbrii
7

Base en la respuesta de kkubasik, verifique si la carpeta existe antes de eliminar, más robusta

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
fuente
66
esto introduce una posible condición de carrera
Corey Goldberg el
1
según most-pythonic-way-to-delete-a-file-which-may-not-exist , es preferible tryeliminar y manejar exceptque llamar exists()primero
TT--
6

si está seguro de que desea eliminar todo el árbol de directorios y ya no está interesado en el contenido del directorio, entonces rastrear todo el árbol de directorios es una estupidez ... simplemente llame al comando nativo del sistema operativo desde Python para hacerlo. Será más rápido, eficiente y consumirá menos memoria.

RMDIR c:\blah /s /q 

o * nix

rm -rf /home/whatever 

En python, el código se verá así ...

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
fuente
33
-1. El objetivo de usar shutil.rmdires aislarlo del tipo de sistema operativo.
mtrw
3
Entiendo el concepto, pero cuando uno es muy consciente del hecho de que desea eliminar la carpeta por completo, entonces, ¿cuál es el punto de rastrear todo el árbol de archivos? shutil.rmdir llama específicamente a os.listdir (), os.path.islink (), etc. algunas comprobaciones que no siempre son necesarias, ya que todo lo que se necesita es desvincular el nodo del sistema de archivos. Además de algunos sistemas de compilación, como MSWindows para el desarrollo de MSAuto / WinCE, shtuil.rmdir fallará casi siempre, ya que el desarrollo basado en lotes de MSAuto bloquea algunos archivos de compilación extraños en la salida fallida, y solo rmdir / S / Q o reiniciar es útil para limpiar ellos.
PM
2
sí, solo rm está más cerca del kernel, usando menos tiempo, memoria y CPU ..... y como dije, la razón por la que usé este método fue por los bloqueos que dejaron los scripts de compilación por lotes de MSAuto ...
PM
3
Sí, pero el uso de shutil hace que el código sea multiplataforma y abstraiga los detalles de la plataforma.
xshoppyx
2
No creo que esta respuesta deba ser rechazada por debajo de 1, ya que proporciona una muy buena referencia para solucionar ciertas situaciones en las que un lector podría estar interesado. Disfruto tener varios métodos publicados con ellos clasificados en orden. Entonces, aunque no necesito usar esto, ahora sé que se puede hacer y cómo.
kmcguire
5

Solo algunas opciones de Python 3.5 para completar las respuestas anteriores. (Me hubiera encantado encontrarlos aquí).

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

Eliminar carpeta si está vacía

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

Eliminar también la carpeta si contiene este archivo

    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)

eliminar carpeta si contiene solo archivos .srt o .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")

Eliminar carpeta si su tamaño es inferior a 400kb:

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
fuente
44
Por favor, corrija la sangría y el códigoif files[0]== "desktop.ini" or:
Mr_and_Mrs_D
5

Me gustaría agregar un enfoque de "pathlib puro":

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

Esto se basa en el hecho de que Pathes ordenable, y las rutas más largas siempre se ordenarán después de las rutas más cortas, al igual que str. Por lo tanto, los directorios vendrán antes que los archivos. Si invertimos el orden, los archivos aparecerán antes de sus respectivos contenedores, por lo que simplemente podemos desvincularlos / rmdirlos uno por uno con una sola pasada.

Beneficios:

  • NO depende de binarios externos: todo usa módulos incluidos con baterías de Python (Python> = 3.6)
  • Es rápido y eficiente en la memoria: sin pila de recursión, sin necesidad de iniciar un subproceso
  • Es multiplataforma (al menos, eso es lo que pathlib promete en Python 3.6; ninguna operación mencionada anteriormente no se ejecuta en Windows)
  • Si es necesario, se puede realizar un registro muy granular, por ejemplo, registrar cada eliminación a medida que sucede.
pepoluan
fuente
¿Puede proporcionar también un ejemplo de uso, por ejemplo. del_dir (Ruta ())? Gracias
lcapra
@lcapra Simplemente llámelo con el directorio para eliminarlo como primer argumento.
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)
increíble
fuente
Es genial hacer un script que pone el archivo en quarenteen antes de eliminarlos ciegamente
racribeiro
3

Si no desea usar el shutilmódulo, simplemente puede usar el osmódulo.

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
fuente
2
os.removeno puede eliminar directorios, por lo que esto aumentará OsErrorsi directoryToRemovecontiene subdirectorios.
Epónimo
#pronetoraceconditions
kapad
3

Diez años después y usando Python 3.7 y Linux todavía hay diferentes maneras de hacer esto:

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

Esencialmente está usando el módulo de subproceso de Python para ejecutar el script bash $ rm -rf '/path/to/your/dircomo si estuviera usando el terminal para realizar la misma tarea. No es totalmente Python, pero lo hace.

La razón por la que incluí el pathlib.Pathejemplo es porque, en mi experiencia, es muy útil cuando se trata de muchos caminos que cambian. Los pasos adicionales para importar el pathlib.Pathmódulo y convertir los resultados finales en cadenas a menudo me cuestan menos tiempo de desarrollo. Sería conveniente si Path.rmdir()viniera con una opción arg para manejar explícitamente directorios no vacíos.

RodogInfinito
fuente
También cambié a este enfoque, porque me encontré con problemas rmtreey carpetas ocultas como .vscode. Esta carpeta se detectó como archivo de texto y el error me dijo que este archivo era busyy no podía eliminarse.
Daniel Eisenreich
2

Para eliminar una carpeta, incluso si no existe (evitando la condición de carrera en la respuesta de Charles Chow ) pero aún tiene errores cuando otras cosas salen mal (por ejemplo, problemas de permisos, error de lectura del disco, el archivo no es un directorio)

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

El código Python 2.7 es casi el mismo:

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)
Epónimo
fuente
1

Con os.walk propondría la solución que consiste en 3 llamadas Python de una línea:

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

El primer script chmod es todos los subdirectorios, el segundo script chmod es todos los archivos. Luego, el tercer script elimina todo sin impedimentos.

He probado esto desde el "Shell Script" en un trabajo de Jenkins (no quería almacenar un nuevo script de Python en SCM, por eso busqué una solución de una línea) y funcionó para Linux y Windows.

Alexander Samoylov
fuente
Con pathlib, puede combinar los dos primeros pasos en uno:[p.chmod(0o666) for p in pathlib.Path(_path_).glob("**/*")]
pepoluan
0

Puede usar el comando os.system para simplificar:

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

Como es obvio, en realidad invoca la terminal del sistema para realizar esta tarea.


fuente
19
Lo sentimos, esto no es pitónico y depende de la plataforma.
Ami Tavory
0

He encontrado una manera muy fácil de eliminar cualquier carpeta (incluso NO vacía) o archivo en el sistema operativo WINDOWS .

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

Para Windows, si el directorio no está vacío y tiene archivos de solo lectura u obtiene errores como

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

Prueba esto, os.system('rmdir /S /Q "{}"'.format(directory))

Es equivalente para rm -rfen Linux / Mac.

Kartik Raj
fuente