【问题标题】:How do I remove/delete a folder that is not empty?如何删除/删除非空文件夹?
【发布时间】:2010-09-23 02:58:48
【问题描述】:

当我尝试删除非空文件夹时,我收到“访问被拒绝”错误。我在尝试中使用了以下命令:os.remove("/folder_name")

移除/删除非空文件夹/目录最有效的方法是什么?

【问题讨论】:

标签: python file


【解决方案1】:
import shutil

shutil.rmtree('/folder_name')

Standard Library Reference: shutil.rmtree.

根据设计,rmtree 在包含只读文件的文件夹树上失败。如果您希望删除文件夹而不管它是否包含只读文件,请使用

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

【讨论】:

  • 注意如果有只读文件rmtree会失败:stackoverflow.com/questions/2656322/…
  • 这对我不起作用:回溯(最近一次调用最后一次):文件“foo.py”,第 31 行,在 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] Directory not empty: '/path/to/rmtree'
  • Clayton:很可能,在 rmtree 忙于删除内容时同时添加了一个文件,“rm -rf”同样会失败。
  • 有人知道为什么这个功能不在 os 包中吗?似乎 os.rmdir 没用。为什么以这种方式实现它有什么好的论据吗?
  • @Malcolm 该包是操作系统功能的包装器。在POSIX 系统上,如果目录不为空,rmdir 将失败。 UbuntuWindows 是这方面 POSIX 合规性的流行示例。
【解决方案2】:

来自the python docsos.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))

【讨论】:

  • 好吧,也许我的降级是错误的。但我可以,现在我觉得是对的。
  • @ddaa:虽然使用 shutil 绝对是最简单的方法,但这个解决方案肯定没有什么不符合 Python 的。我不会赞成这个答案,但我有这个时间只是为了取消你的反对票:)
  • 代码本身是pythonic。在真正的程序中使用它而不是 shutil.rmtree 将是非pythonic:那将忽略“一种明显的做法”。无论如何,这是语义,删除downmod。
  • @ddaa 想要记录每个被删除的文件或目录是不是很奇怪?我不确定如何用 shutil.rmtree 做到这一点?
  • @ddaa 这是令人深思的食物,即修辞。我知道我在做什么。我只是想你可能想重新考虑“做这件事的明显方式”,提供一个为什么 shutil.rmtree 可能不是正确的“适合”的原因。
【解决方案3】:
import shutil
shutil.rmtree(dest, ignore_errors=True)

【讨论】:

  • 这是正确答案。在我的系统中,即使我将特定文件夹中的所有内容都设置为可读写,但在尝试删除时仍会出现错误。 ignore_errors=True 解决问题。
  • 在我的回答中使用onerror 参数而不是ignore_errors。这样只读文件会被删除而不是被忽略。
  • 是的,这不会在出错时删除文件。所以基本上整个rmtree()方法都被忽略了。
  • 这应该是对 6 年前接受的答案的一个小修改,而不是一个新的答案。我现在就这样做。
【解决方案4】:

从 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 the dir content but not the dir itself, remove this line

其中pthpathlib.Path 实例。不错,但可能不是最快的。

【讨论】:

    【解决方案5】:

    来自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)
    

    【讨论】:

      【解决方案6】:
      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 为 false 且 onerror 为 None,则会引发异常。在此处输入代码

      【讨论】:

      • 根据docsonerror 引发的异常不会被捕获 所以我不确定你的 raise enter code here 意味着什么.
      • -1。与戴夫钱德勒的回答相比,这似乎过于复杂。另外,如果我们想删除只读,我们不需要使文件可执行。
      【解决方案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")
      

      【讨论】:

      【解决方案8】:

      只需一些 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")
      

      如果文件夹大小小于 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")
      

      【讨论】:

      • 请修正缩进和代码if files[0]== "desktop.ini" or:
      【解决方案9】:

      我想添加一个“纯路径库”方法:

      from pathlib import Path
      from typing import Union
      
      def del_dir(target: Union[Path, str], only_if_empty: bool = False):
          """
          Delete a given directory and its subdirectories.
      
          :param target: The directory to delete
          :param only_if_empty: Raise RuntimeError if any file is found in the tree
          """
          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 只需将要删除的目录作为第一个参数调用它。
      • “它快速且节省内存:没有递归堆栈,无需启动子进程” - 实际上并非如此。递归通配中仍然存在递归。它也不节省内存,因为您生成了两个包含所有文件和文件夹路径的列表:sorted 内置函数首先生成由 glob 生成器返回的项目列表,然后生成一个包含排序项目的新列表.根据文件的数量,这可能会导致大量内存消耗。哦,你正在引入一个具有n log n 时间复杂度的排序。
      • @danzel 你在技术上是正确的。我会编辑我的答案以免误导。
      • @danzel 说,我认为排序不会比使用os.systemsubprocess.run 重复启动子进程来运行shell 命令慢。此外,维护列表 + 排序列表所需的内存可能小于启动子进程并运行它所需的内存。 YMMV
      【解决方案10】:

      如果您确定要删除整个 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])
      

      【讨论】:

      • -1。使用shutil.rmdir 的全部意义在于将您与操作系统的类型隔离开来。
      • 我理解这个概念,但是当一个人很清楚他想要完全删除文件夹这一事实时,那么爬取整个文件树有什么意义呢? shutil.rmdir 专门调用 os.listdir()、os.path.islink() 等等等等。一些并不总是需要的检查,因为只需要取消链接文件系统节点。除了在一些构建系统上,比如用于 MSAuto/WinCE 开发的 MSWindows,那么 shtuil.rmdir 几乎总是会失败,因为基于 MSAuto 批处理的开发会在不成功退出时锁定一些奇怪的构建文件,只有 rmdir /S/Q 或重新启动有助于清理他们。
      • 是的,只是 rm 更接近内核,使用更少的时间、内存和 cpu .....正如我所说,我使用这种方法的原因是因为 MSAuto 留下了锁批量构建脚本 ...
      • 是的,但是使用shutil可以使代码跨平台并抽象出平台细节。
      • 我认为这个答案不应该被否决低于 1,因为它为读者可能感兴趣的某些情况下的解决方法提供了一个很好的参考。我喜欢发布多种方法他们按顺序排列。因此,即使我不需要使用它,我现在也知道它可以完成以及如何完成。
      【解决方案11】:

      十年后,使用 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() 带有一个 arg 选项来显式处理非空目录会很方便。

      【讨论】:

      • 我也改用了这种方法,因为我遇到了rmtree.vscode 等隐藏文件夹的问题。此文件夹被检测为文本文件,错误告诉我此文件为 busy,无法删除。
      【解决方案12】:
      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)
      

      【讨论】:

      • 非常适合制作将文件放入 quarenteen 的脚本,然后再盲目删除它们。
      【解决方案13】:

      如果您不想使用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
      

      【讨论】:

      • os.remove 无法删除目录,因此如果directoryToRemove 包含子目录,则会引发OsError
      • #pronetoraceconditions
      【解决方案14】:

      删除一个文件夹,即使它可能不存在(避免Charles Chow's answer 中的竞争条件)但在其他事情出错时仍然有错误(例如权限问题,磁盘读取错误,文件不是目录)

      对于 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)
      

      【讨论】:

        【解决方案15】:

        对于 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 的所有文件。然后第三个脚本毫无障碍地删除所有内容。

        我在 Jenkins 工作中通过“Shell 脚本”对此进行了测试(我不想将新的 Python 脚本存储到 SCM 中,这就是搜索单行解决方案的原因),它适用于 Linux 和 Windows。

        【讨论】:

        • 使用pathlib,您可以将前两个步骤合二为一:[p.chmod(0o666) for p in pathlib.Path(_path_).glob("**/*")]
        【解决方案16】:

        对于 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))

        相当于 Linux/Mac 中的rm -rf

        【讨论】:

          【解决方案17】:

          基于递归的纯pathlib解决方案:

          from pathlib import Path
          
          def remove_path(path: Path):
              if path.is_file() or path.is_symlink():
                  path.unlink()
                  return
              for p in path.iterdir():
                  remove_path(p)
              path.rmdir()
          

          支持 Windows 和符号链接

          【讨论】:

            【解决方案18】:

            为简单起见,您可以使用 os.system 命令:

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

            很明显,它实际上是调用系统终端来完成这个任务。

            【讨论】:

            • 抱歉,这是 Unpythonic 和平台相关的。
            【解决方案19】:

            我找到了一种非常简单的方法来删除 WINDOWS OS 上的任何文件夹(甚至不是空的) 或文件。

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

            【讨论】:

              【解决方案20】:

              就我而言,删除的唯一方法是使用所有可能性,因为我的代码应该由 cmd.exe 或 powershell.exe 运行。如果是您的情况,只需使用此代码创建一个函数就可以了:

                      #!/usr/bin/env python3
              
                      import shutil
                      from os import path, system
                      import sys
              
                      # Try to delete the folder ---------------------------------------------
                      if (path.isdir(folder)):
                          shutil.rmtree(folder, ignore_errors=True)
              
                      if (path.isdir(folder)):
                          try:
                              system("rd -r {0}".format(folder))
                          except Exception as e:
                              print("WARN: Failed to delete => {0}".format(e),file=sys.stderr)
              
                      if (path.isdir(self.backup_folder_wrk)):
                          try:
                              system("rd /s /q {0}".format(folder))
                          except Exception as e:
                              print("WARN: Failed to delete => {0}".format(e),file=sys.stderr)
              
                      if (path.isdir(folder)):
                          print("WARN: Failed to delete {0}".format(folder),file=sys.stderr)
                      # -------------------------------------------------------------------------------------
              
              

              【讨论】:

                猜你喜欢
                • 2014-02-01
                • 2012-06-18
                • 2020-07-23
                • 2017-02-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-07-05
                相关资源
                最近更新 更多