【问题标题】:os.walk without digging into directories belowos.walk 没有深入到下面的目录
【发布时间】:2010-09-18 18:07:36
【问题描述】:

如何限制os.walk 只返回我提供的目录中的文件?

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
    return outputList

【问题讨论】:

  • 另一种情况,多种可能的方法以及随之而来的所有注意事项表明应该将此功能添加到 Python 标准库中。
  • files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]。如果您只需要文件名,请使用 f.name 而不是 f.path。这是最快的解决方案,比任何walklistdir 都快得多,请参阅stackoverflow.com/a/40347279/2441026

标签: python file os.walk


【解决方案1】:

这是一个不错的 Python 示例

def walk_with_depth(root_path, depth):
        if depth < 0:
            for root, dirs, files in os.walk(root_path):
                yield [root, dirs[:], files]

            return

        elif depth == 0:
            return

        base_depth = root_path.rstrip(os.path.sep).count(os.path.sep)
        for root, dirs, files in os.walk(root_path):
            yield [root, dirs[:], files]

            cur_depth = root.count(os.path.sep)
            
            if base_depth + depth <= cur_depth:
                del dirs[:]

【讨论】:

    【解决方案2】:
    import os
    
    def listFiles(self, dir_name):
        names = []
        for root, directory, files in os.walk(dir_name):
            if root == dir_name:
                for name in files:
                    names.append(name)
        return names
    

    【讨论】:

    • 嗨 Rich,欢迎来到 Stack Overflow!感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 它的长期价值,通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
    【解决方案3】:

    os.walk 找到的每个目录都会更改根文件夹。我求解检查是否 root == 目录

    def _dir_list(self, dir_name, whitelist):
        outputList = []
        for root, dirs, files in os.walk(dir_name):
            if root == dir_name: #This only meet parent folder
                for f in files:
                    if os.path.splitext(f)[1] in whitelist:
                        outputList.append(os.path.join(root, f))
                    else:
                        self._email_to_("ignore")
        return outputList
    

    【讨论】:

      【解决方案4】:

      Python 3.5 开始,您可以使用 os.scandir 代替 os.listdir。作为回报,您将获得 DirEntry 对象的迭代器,而不是字符串。来自文档:

      使用scandir() 代替listdir() 可以显着提高还需要文件类型或文件属性信息的代码的性能,因为如果操作系统在扫描目录时提供了DirEntry 对象会公开此信息。所有DirEntry 方法都可以执行系统调用,但is_dir()is_file() 通常只需要对符号链接进行系统调用; DirEntry.stat() 在 Unix 上总是需要一个系统调用,但在 Windows 上只需要一个用于符号链接。

      您可以通过DirEntry.name 访问对象的名称,这相当于os.listdir 的输出

      【讨论】:

      【解决方案5】:

      使用listdir 的建议很好。在 Python 2 中您的问题的直接答案是 root, dirs, files = os.walk(dir_name).next()

      等效的 Python 3 语法是 root, dirs, files = next(os.walk(dir_name))

      【讨论】:

      • 哦,我从那个错误中得到了各种有趣的错误。 ValueError:解包的值太多
      • 不错!感觉就像一个黑客,虽然。就像你打开引擎但只让它转一圈然后拉动钥匙让它死。
      • 偶然发现了这个; root, dirs, files = os.walk(dir_name).next() 给我AttributeError: 'generator' object has no attribute 'next'
      • @Evan,可能是因为这是从 2008 年开始并使用 Python 2 语法。在 Python 3 中,您可以编写 root, dirs, files = next(os.walk(dir_name)),然后变量 root, dirs, files 将仅对应于 dir_name 级别的生成器变量。
      【解决方案6】:

      对亚历克斯的回答略有改动,但使用__next__()

      print(next(os.walk('d:/'))[2]) 要么 print(os.walk('d:/').__next__()[2])

      [2] 是其他答案中提到的root, dirs, file 中的file

      【讨论】:

        【解决方案7】:

        为什么不简单地将rangeos.walkzip 结合使用?不是最好的解决方案,但也可以。

        例如这样:

        # your part before
        for count, (root, dirs, files) in zip(range(0, 1), os.walk(dir_name)):
            # logic stuff
        # your later part
        

        在 python 3 上为我工作。

        另外:break 也更简单。 (看@Pieter的回答)

        【讨论】:

          【解决方案8】:

          创建一个排除列表,使用 fnmatch 跳过目录结构并执行该过程

          excludes= ['a\*\b', 'c\d\e']
          for root, directories, files in os.walk('Start_Folder'):
              if not any(fnmatch.fnmatch(nf_root, pattern) for pattern in excludes):
                  for root, directories, files in os.walk(nf_root):
                      ....
                      do the process
                      ....
          

          与“包含”相同:

          if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):
          

          【讨论】:

            【解决方案9】:

            我觉得解决方法其实很简单。

            使用

            break
            

            只做for循环的第一次迭代,必须有更优雅的方式。

            for root, dirs, files in os.walk(dir_name):
                for f in files:
                    ...
                    ...
                break
            ...
            

            第一次调用 os.walk 时,它会返回当前目录的郁金香,然后在下一个循环中返回下一个目录的内容。

            获取原始脚本并添加一个break

            def _dir_list(self, dir_name, whitelist):
                outputList = []
                for root, dirs, files in os.walk(dir_name):
                    for f in files:
                        if os.path.splitext(f)[1] in whitelist:
                            outputList.append(os.path.join(root, f))
                        else:
                            self._email_to_("ignore")
                    break
                return outputList
            

            【讨论】:

            • 这应该是公认的答案。只需在“for f in files”循环之后添加“break”即可停止递归。您可能还需要确保 topdown=True。
            • 我只想添加此评论,并感谢您为我提供了如此简单的答案,为我节省了工作时间。
            【解决方案10】:

            感觉就像把我的 2 便士扔进去。

            baselevel = len(rootdir.split("\\"))
            for subdirs, dirs, files in os.walk(rootdir):
                curlevel = len(subdirs.split("\\"))
                if curlevel <= baselevel + 1:
                    [do stuff]
            

            【讨论】:

            • 有帮助,除了 "\\" 假定 Windoze 操作系统。使用os.path.sep
            【解决方案11】:

            你可以用这个sn-p

            for root, dirs, files in os.walk(directory):
                if level > 0:
                    # do some stuff
                else:
                    break
                level-=1
            

            【讨论】:

              【解决方案12】:
              for path, dirs, files in os.walk('.'):
                  print path, dirs, files
                  del dirs[:] # go only one level deep
              

              【讨论】:

                【解决方案13】:

                在 Python 3 中,我能够做到这一点:

                import os
                dir = "/path/to/files/"
                
                #List all files immediately under this folder:
                print ( next( os.walk(dir) )[2] )
                
                #List all folders immediately under this folder:
                print ( next( os.walk(dir) )[1] )
                

                【讨论】:

                • 这也适用于Python 2。如何获得第二级?
                【解决方案14】:

                listdir 相同,但更短:

                [f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]
                

                【讨论】:

                  【解决方案15】:

                  使用 listdir 时有一个问题。 os.path.isdir(identifier) 必须是绝对路径。要选择您所做的子目录:

                  for dirname in os.listdir(rootdir):
                    if os.path.isdir(os.path.join(rootdir, dirname)):
                       print("I got a subdirectory: %s" % dirname)
                  

                  另一种方法是在不使用 os.path.join() 的情况下切换到目录进行测试。

                  【讨论】:

                    【解决方案16】:

                    我就是这样解决的

                    if recursive:
                        items = os.walk(target_directory)
                    else:
                        items = [next(os.walk(target_directory))]
                    
                    ...
                    

                    【讨论】:

                      【解决方案17】:

                      您还可以执行以下操作:

                      for path, subdirs, files in os.walk(dir_name):
                          for name in files:
                              if path == ".": #this will filter the files in the current directory
                                   #code here
                      

                      【讨论】:

                      • 这不会不必要地遍历所有子目录和文件吗?
                      【解决方案18】:

                      如果您有比顶级目录更复杂的要求(例如忽略 VCS 目录等),您还可以修改目录列表以防止 os.walk 递归遍历它们。

                      即:

                      def _dir_list(self, dir_name, whitelist):
                          outputList = []
                          for root, dirs, files in os.walk(dir_name):
                              dirs[:] = [d for d in dirs if is_good(d)]
                              for f in files:
                                  do_stuff()
                      

                      注意 - 小心改变列表,而不是重新绑定它。显然 os.walk 不知道外部重新绑定。

                      【讨论】:

                        【解决方案19】:

                        使用walklevel 函数。

                        import os
                        
                        def walklevel(some_dir, level=1):
                            some_dir = some_dir.rstrip(os.path.sep)
                            assert os.path.isdir(some_dir)
                            num_sep = some_dir.count(os.path.sep)
                            for root, dirs, files in os.walk(some_dir):
                                yield root, dirs, files
                                num_sep_this = root.count(os.path.sep)
                                if num_sep + level <= num_sep_this:
                                    del dirs[:]
                        

                        它的工作方式与os.walk 类似,但您可以向它传递一个level 参数,指示递归的深度。

                        【讨论】:

                        • 这个函数是否真的“遍历”整个结构,然后删除某个点以下的条目?还是发生了更聪明的事情?我什至不确定如何用代码检查这一点。 --python 初学者
                        • @mathtick:当找到某个位于或低于所需级别的目录时,它的所有子目录都将从子目录列表中删除以进行下一步搜索。所以他们不会被“走路”。
                        • 我只是为此 +1 了,因为我一直在为如何“删除”目录而苦苦挣扎。我曾尝试过dirs = []dirs = None,但这些都不起作用。 map(dirs.remove, dirs) 工作,但打印了一些不需要的“[None]”消息。那么,为什么是 del dirs[:]
                        • 请注意,这在 os.walk 中使用 topdown=False 时不起作用。见docs中的第4段:Modifying dirnames when topdown is False has no effect on the behavior of the walk, because in bottom-up mode the directories in dirnames are generated before dirpath itself is generated.
                        • @ZacharyYoung dirs = []dirs = None 不起作用,因为它们只是创建了一个新的不相关对象并分配给名称 dirs。需要就地修改原始列表对象,而不是名称dirs
                        【解决方案20】:

                        不要使用 os.walk。

                        例子:

                        import os
                        
                        root = "C:\\"
                        for item in os.listdir(root):
                            if os.path.isfile(os.path.join(root, item)):
                                print item
                        

                        【讨论】:

                        • @576i:这不区分文件和目录
                        • @Alexandr os.path.isfileos.path.isdir 让您与众不同。我不明白,因为 os.path.isfile 自 08 年以来就在示例代码中,而您的评论来自 16 年。这显然是更好的答案,因为您不打算遍历目录,而是要列出它。
                        • @DanielF,我的意思是你需要遍历所有项目,而walk 会立即为你提供单独的目录和文件列表。
                        • 啊,好的。实际上,Alex 的答案似乎更好(使用.next()),并且更接近您的想法。
                        • Python 3.5 有一个os.scandir 函数,它允许更复杂的文件或目录对象交互。请参阅下面的my answer
                        【解决方案21】:

                        您可以使用os.listdir() 返回给定目录中的名称列表(文件和目录)。如果您需要区分文件和目录,请在每个名称上调用os.stat()

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2017-05-31
                          • 2017-08-05
                          • 2012-10-25
                          • 2018-08-10
                          • 1970-01-01
                          • 2011-07-05
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多