77 votes

Shutil.rmtree échoue sur Windows avec 'Accès refusé'

En Python, lors de l'exécution de shutil.rmtree sur un dossier contenant un fichier en lecture seule, l'exception suivante est affichée :

 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 216, dans rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", ligne 221, dans rmtree
   onerror(os.remove, fullname, sys.exc_info())
 File "C:\Python26\lib\shutil.py", ligne 219, dans rmtree
   os.remove(fullname)
WindowsError: [Error 5] Access is denied: 'build\\tcl\\tcl8.5\\msgs\\af.msg'

En regardant dans la boîte de dialogue Propriétés du fichier, j'ai remarqué que le fichier af.msg est défini en lecture seule.

La question est donc : quel est le contournement/correctif le plus simple pour contourner ce problème - sachant que mon intention est de faire l'équivalent de rm -rf build/ mais sur Windows? (sans avoir à utiliser des outils tiers comme unxutils ou cygwin - car ce code est destiné à être exécuté sur une installation Windows de base avec Python 2.6 et PyWin32 installé)

5 votes

shutil.rmtree utilise os.remove pour supprimer des fichiers. os.remove supprime très bien les fichiers en lecture seule (du moins sur Unix). os.remove ne peut pas supprimer un fichier sous Windows s'il est en cours d'utilisation.

0 votes

Comme je l'ai vécu, peut-être que cette erreur apparaîtra si le répertoire est ouvert et que vous exécutez le code et est liée au processus de suppression, et non à l'étape de création.

98voto

Justin Peel Points 17348

Vérifiez cette question : Quel utilisateur exécutent les scripts python sous windows?

Apparemment, la réponse est de modifier le fichier/dossier pour qu'il ne soit pas en lecture seule, puis de le supprimer.

Voici le gestionnaire onerror() de pathutils.py mentionné par @Sridhar Ratnakumar dans les commentaires :

def onerror(func, path, exc_info):
    """
    Gestionnaire d'erreurs pour ``shutil.rmtree``.

    Si l'erreur est due à une erreur d'accès (fichier en lecture seule)
    il tente d'ajouter la permission d'écriture puis réessaye.

    Si l'erreur est due à une autre raison, il relance l'erreur.

    Utilisation : ``shutil.rmtree(path, onerror=onerror)``
    """
    import stat
    # L'erreur est-elle une erreur d'accès?
    if not os.access(path, os.W_OK):
        os.chmod(path, stat.S_IWUSR)
        func(path)
    else:
        raise

1 votes

Heh. Je viens de découvrir le gestionnaire onerror sur voidspace.org.uk/downloads/pathutils.py

0 votes

2 votes

Même si les commentaires pour cette réponse disent "changer le fichier/dossier pour qu'il ne soit pas en lecture seule", j'ai quand même reçu un accès refusé sur des dossiers en lecture seule. Cette implémentation a fonctionné, cependant.

33voto

Epcylon Points 2548

Je dirais de mettre en place votre propre rmtree avec os.walk qui garantit l'accès en utilisant os.chmod sur chaque fichier avant de tenter de le supprimer.

Quelque chose comme ceci (non testé) :

import os
import stat

def rmtree(top):
    for root, dirs, files in os.walk(top, topdown=False):
        for name in files:
            filename = os.path.join(root, name)
            os.chmod(filename, stat.S_IWUSR)
            os.remove(filename)
        for name in dirs:
            os.rmdir(os.path.join(root, name))
    os.rmdir(top)

1 votes

C'est presque correct - Windows ne supporte que stat.S_IWRITE (ce que vous voulez de toute façon) - docs.python.org/library/os.html#os.chmod

1 votes

J'ai testé que os.chmod(filename, stat.S_IWUSR) a supprimé le drapeau en lecture seule, donc ça fonctionne sur WinXP. Et si l'on considère ce que disent les docs concernant stat.S_IWRITE : "Synonyme Unix V7 pour S_IWUSR" (docs.python.org/library/stat.html#stat.S_IWRITE), je pense que mon code est correct de toute façon.

0 votes

Génial, avec des chemins de fichiers trop longs, cela semble être la seule solution. Une recommandation à envisager consiste à s'engager à modifier shutil.rmtree peut-être.

22voto

AlexeiOst Points 116

Eh bien, la solution marquée n'a pas fonctionné pour moi... j'ai fait cela à la place :

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

0 votes

Cela a supprimé le répertoire lui-même. Pouvez-vous s'il vous plaît me dire comment supprimer tous les répertoires et fichiers à l'intérieur d'un répertoire? Par exemple, si je donne le chemin : monprojet/dir1/, alors il supprime dir1 mais je veux supprimer tout ce qui se trouve sous dir1.

1voto

RongyanZheng Points 129
shutil.rmtree(chemin,ignore_errors=False,onerror=errorRemoveReadonly) 
def errorRemoveReadonly(func, chemin, 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(chemin, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)  
        # retry
        func(chemin)
    else:
        raise

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 les arguments (func, chemin, exc_info) où func est os.listdir, os.remove, ou os.rmdir; chemin est l'argument de cette fonction qui l'a fait échouer; et exc_info est un tuple renvoyé par sys.exc_info(). Si ignore_errors est faux et onerror est None, une exception est levée.

-5voto

besil Points 54

Si vous exécutez votre script en utilisant cygwin, vous pouvez utiliser subprocess.call

from subprocess import call
call("rm -rf build/", shell=True)

Bien sûr, cela fonctionne uniquement à l'intérieur de l'émulateur cygwin/bash.

2 votes

Appeler rm -rf sur Windows? Je ne pense pas.

0 votes

Très étrange. J'utilise un émulateur de console de type unix pour Windows (cmder). L'approche subprocess.call fonctionne lorsque j'exécute le script à partir de cette console, mais pas si je l'exécute à partir de l'invite de commandes par défaut "Command Prompt".

0 votes

Avez-vous déjà essayé de voter avant de downvoter? Je confirme que cela fonctionne sous Windows

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X