【问题标题】:Get names of all files from a folder with Ruby使用 Ruby 从文件夹中获取所有文件的名称
【发布时间】:2010-12-17 20:38:19
【问题描述】:

我想使用 Ruby 从一个文件夹中获取所有文件名。

【问题讨论】:

    标签: ruby file directory filenames


    【解决方案1】:

    您还可以使用

    的快捷方式选项
    Dir["/path/to/search/*"]
    

    如果您想在任何文件夹或子文件夹中查找所有 Ruby 文件:

    Dir["/path/to/search/**/*.rb"]
    

    【讨论】:

    • 或者你可以用 Dir::glob() 做同样的事情
    • 另外,使用./... 而不是~/
    • 为什么首选这个?
    • @MinhTriet 那是做什么的?哪个更可取?
    • @marflar - ./ 表示当前目录,而/ 是根挂载点,~/ 是用户的主目录。如果您将整个项目移到其他地方,第一个可以工作,但其他两个可能不会。
    【解决方案2】:
    Dir.entries(folder)
    

    示例:

    Dir.entries(".")
    

    来源:http://ruby-doc.org/core/classes/Dir.html#method-c-entries

    【讨论】:

    • 看起来他正在使用 SO 来记录他刚刚提出的问题的答案。一种备忘录,我想。看不出有什么问题 - 毕竟,即使这个有点不完整(例如,Dir#glob 可能已经被提及)没有什么可以阻止其他人发布一个非常好的答案。 '当然,我主要是一个“玻璃杯半满”的人......
    • @Mike:从总体上看,这可能没什么大不了的。正如您所说,如果问题和答案都很好,那么总体上对网站来说可能是一个加分项。但是这里的问题和答案都太少了,看起来并不是特别有用。
    • @Telemachus 我很少使用Dir,每次需要时我都必须阅读文档。我已经在这里发布了我的问题和答案,以便我以后可以找到它,甚至可以帮助有同样问题的人。我想我在 SO 播客上听说这种行为没有错。如果您有更好的答案,请发布。我已经发布了我所知道的,我不是 Ruby 忍者。我经常接受得票最多的答案。
    • 当参数是变量时,这可能是比Dir[]Dir.glob 更好的选择。当path = '/tmp' 时,比较:Dir.glob("#{path}/*")Dir.entries(path)。返回值略有不同(“.”、“..”),但后者更容易快速浏览。
    • Dir.entries 将返回一个包含“.”的列表。和 ”..”。如果你不想要这些,你可以使用 Dir.each_child。
    【解决方案3】:

    下面的 sn-ps 准确显示了目录中的文件名,跳过了子目录和"."".." 虚线文件夹:

    Dir.entries("your/folder").select { |f| File.file? File.join("your/folder", f) }
    

    【讨论】:

    • 也可以使用...select {|f| File.file? f} 以获得更清晰的含义和更短的语法。
    • 我看到 Dir.entries 没有给出返回文件名的完整路径,所以这个解决方案对我不起作用。
    • @squixy 你写对了吗?:Dir.entries("your/folder").select {|f| File.file? f}
    • 是的。 !File.directory? 正在工作,但 File.file? 没有。
    • .reject {|f| File.directory? f} 似乎比 .select{|f| !File.directory? f} 干净。哦,现在我看到了第一条评论……也不错。
    【解决方案4】:

    递归获取所有文件(仅限文件):

    Dir.glob('path/**/*').select { |e| File.file? e }
    

    或者任何不是目录的东西(File.file? 会拒绝非常规文件):

    Dir.glob('path/**/*').reject { |e| File.directory? e }
    

    替代解决方案

    使用Find#find 而不是像Dir.glob 这样的基于模式的查找方法实际上更好。见this answer to "One-liner to Recursively List Directories in Ruby?"

    【讨论】:

      【解决方案5】:

      这对我有用:

      如果您不想隐藏文件[1],请使用 Dir[]

      # With a relative path, Dir[] will return relative paths 
      # as `[ './myfile', ... ]`
      #
      Dir[ './*' ].select{ |f| File.file? f } 
      
      # Want just the filename?
      # as: [ 'myfile', ... ]
      #
      Dir[ '../*' ].select{ |f| File.file? f }.map{ |f| File.basename f }
      
      # Turn them into absolute paths?
      # [ '/path/to/myfile', ... ]
      #
      Dir[ '../*' ].select{ |f| File.file? f }.map{ |f| File.absolute_path f }
      
      # With an absolute path, Dir[] will return absolute paths:
      # as: [ '/home/../home/test/myfile', ... ]
      #
      Dir[ '/home/../home/test/*' ].select{ |f| File.file? f }
      
      # Need the paths to be canonical?
      # as: [ '/home/test/myfile', ... ]
      #
      Dir[ '/home/../home/test/*' ].select{ |f| File.file? f }.map{ |f| File.expand_path f }
      

      现在,Dir.entries 将返回隐藏文件,并且您不需要通配符 asterix(您可以只传递带有目录名称的变量),但它会直接返回基本名称,所以 File.xxx 函数不起作用。

      # In the current working dir:
      #
      Dir.entries( '.' ).select{ |f| File.file? f }
      
      # In another directory, relative or otherwise, you need to transform the path 
      # so it is either absolute, or relative to the current working dir to call File.xxx functions:
      #
      home = "/home/test"
      Dir.entries( home ).select{ |f| File.file? File.join( home, f ) }
      

      [1] .dotfile 在 unix 上,我不了解 Windows

      【讨论】:

        【解决方案6】:

        在 Ruby 2.5 中,您现在可以使用 Dir.children。它将文件名作为数组获取,“.”除外和“..”

        例子:

        Dir.children("testdir")   #=> ["config.h", "main.rb"]
        

        http://ruby-doc.org/core-2.5.0/Dir.html#method-c-children

        【讨论】:

          【解决方案7】:

          就个人而言,我发现这对于循环遍历文件夹中的文件最有用,具有前瞻性:

          Dir['/etc/path/*'].each do |file_name|
            next if File.directory? file_name 
          end
          

          【讨论】:

            【解决方案8】:

            这是在目录中查找文件的解决方案:

            files = Dir["/work/myfolder/**/*.txt"]
            
            files.each do |file_name|
              if !File.directory? file_name
                puts file_name
                File.open(file_name) do |file|
                  file.each_line do |line|
                    if line =~ /banco1/
                      puts "Found: #{line}"
                    end
                  end
                end
              end
            end
            

            【讨论】:

              【解决方案9】:

              在获取目录中的所有文件名时,此 sn-p 可用于拒绝目录 [., ..] 和以 . 开头的隐藏文件

              files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
              

              【讨论】:

              • Dir.entries 返回本地文件名,而不是绝对文件路径。另一方面,File.directory? 需要一个绝对文件路径。此代码未按预期工作。
              • 奇怪的是代码在你的情况下不起作用。因为这是我在一个运行良好的应用程序中使用的代码。如果我的原始工作代码中缺少任何内容,我将重新检查我的代码并在此处发布:)
              • @Nathan 查看我的答案以获得解释
              • 此答案与 2013 年添加的 the answer 重复
              【解决方案10】:

              此代码仅返回带有扩展名的文件名(没有全局路径)

              Dir.children("/path/to/search/")
              

              【讨论】:

                【解决方案11】:

                这对我有用:

                Dir.entries(dir).select { |f| File.file?(File.join(dir, f)) }
                

                Dir.entries 返回一个字符串数组。然后,我们必须提供文件的完整路径到File.file?,除非dir 等于我们当前的工作目录。这就是为什么File.join()

                【讨论】:

                • 您需要排除“.”和条目中的“..”
                • 此答案与 2013 年添加的 the answer 重复
                【解决方案12】:

                您可能还想使用Rake::FileList(前提是您有rake 依赖项):

                FileList.new('lib/*') do |file|
                  p file
                end
                

                根据 API:

                文件列表是惰性的。当给出可能的 glob 模式列表时 要包含在文件列表中的文件,而不是搜索文件 结构来查找文件,FileList 保存后者的模式 使用。

                https://docs.ruby-lang.org/en/2.1.0/Rake/FileList.html

                【讨论】:

                  【解决方案13】:
                  Dir.new('/home/user/foldername').each { |file| puts file }
                  

                  【讨论】:

                    【解决方案14】:

                    一种简单的方法可能是:

                    dir = './' # desired directory
                    files = Dir.glob(File.join(dir, '**', '*')).select{|file| File.file?(file)}
                    
                    files.each do |f|
                        puts f
                    end
                    

                    【讨论】:

                      【解决方案15】:

                      如果您想获得文件名数组包括符号链接,请使用

                      Dir.new('/path/to/dir').entries.reject { |f| File.directory? f }
                      

                      甚至

                      Dir.new('/path/to/dir').reject { |f| File.directory? f }
                      

                      如果您想不使用符号链接,请使用

                      Dir.new('/path/to/dir').select { |f| File.file? f }
                      

                      如其他答案所示,如果要递归获取所有文件,请使用Dir.glob('/path/to/dir/**/*') 而不是Dir.new('/path/to/dir')

                      【讨论】:

                      • 或者直接使用*.*
                      【解决方案16】:

                      除了这个线程中的建议之外,我想提一下,如果您还需要返回点文件(.gitignore 等),那么使用 Dir.glob 您需要像这样包含一个标志: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) 默认情况下,Dir.entries 包括点文件,以及当前的父目录。

                      对于任何感兴趣的人,我很好奇这里的答案在执行时间上是如何相互比较的,这里是针对深度嵌套层次结构的结果。前三个结果是非递归的:

                             user     system      total        real
                      Dir[*]: (34900 files stepped over 100 iterations)
                        0.110729   0.139060   0.249789 (  0.249961)
                      Dir.glob(*): (34900 files stepped over 100 iterations)
                        0.112104   0.142498   0.254602 (  0.254902)
                      Dir.entries(): (35600 files stepped over 100 iterations)
                        0.142441   0.149306   0.291747 (  0.291998)
                      Dir[**/*]: (2211600 files stepped over 100 iterations)
                        9.399860  15.802976  25.202836 ( 25.250166)
                      Dir.glob(**/*): (2211600 files stepped over 100 iterations)
                        9.335318  15.657782  24.993100 ( 25.006243)
                      Dir.entries() recursive walk: (2705500 files stepped over 100 iterations)
                       14.653018  18.602017  33.255035 ( 33.268056)
                      Dir.glob(**/*, File::FNM_DOTMATCH): (2705500 files stepped over 100 iterations)
                       12.178823  19.577409  31.756232 ( 31.767093)
                      

                      这些是使用以下基准测试脚本生成的:

                      require 'benchmark'
                      base_dir = "/path/to/dir/"
                      n = 100
                      Benchmark.bm do |x|
                        x.report("Dir[*]:") do
                          i = 0
                          n.times do
                            i = i + Dir["#{base_dir}*"].select {|f| !File.directory? f}.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                        x.report("Dir.glob(*):") do
                          i = 0
                          n.times do
                            i = i + Dir.glob("#{base_dir}/*").select {|f| !File.directory? f}.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                        x.report("Dir.entries():") do
                          i = 0
                          n.times do
                            i = i + Dir.entries(base_dir).select {|f| !File.directory? File.join(base_dir, f)}.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                        x.report("Dir[**/*]:") do
                          i = 0
                          n.times do
                            i = i + Dir["#{base_dir}**/*"].select {|f| !File.directory? f}.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                        x.report("Dir.glob(**/*):") do
                          i = 0
                          n.times do
                            i = i + Dir.glob("#{base_dir}**/*").select {|f| !File.directory? f}.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                        x.report("Dir.entries() recursive walk:") do
                          i = 0
                          n.times do
                            def walk_dir(dir, result)
                              Dir.entries(dir).each do |file|
                                next if file == ".." || file == "."
                      
                                path = File.join(dir, file)
                                if Dir.exist?(path)
                                  walk_dir(path, result)
                                else
                                  result << file
                                end
                              end
                            end
                            result = Array.new
                            walk_dir(base_dir, result)
                            i = i + result.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                        x.report("Dir.glob(**/*, File::FNM_DOTMATCH):") do
                          i = 0
                          n.times do
                            i = i + Dir.glob("#{base_dir}**/*", File::FNM_DOTMATCH).select {|f| !File.directory? f}.length
                          end
                          puts " (#{i} files stepped over #{n} iterations)"
                        end
                      end
                      

                      文件计数的差异是由于Dir.entries 默认包含隐藏文件。 Dir.entries 在这种情况下最终花费了更长的时间,因为需要重建文件的绝对路径以确定文件是否为目录,但即使没有,它仍然比递归情况下的其他选项花费更长的时间.这都是在 OSX 上使用 ruby​​ 2.5.1。

                      【讨论】:

                        【解决方案17】:

                        加载操作目录下所有文件名时可以使用

                        Dir.glob("*)

                        这将返回应用程序运行的上下文中的所有文件(注意,Rails 这是应用程序的*目录)

                        您可以在 https://ruby-doc.org/core-2.7.1/Dir.html#method-c-glob 处找到额外的匹配和递归搜索

                        【讨论】:

                          【解决方案18】:
                          def get_path_content(dir)
                            queue = Queue.new
                            result = []
                            queue << dir
                            until queue.empty?
                              current = queue.pop
                              Dir.entries(current).each { |file|
                                full_name = File.join(current, file)
                                if not (File.directory? full_name)
                                  result << full_name
                                elsif file != '.' and file != '..'
                                    queue << full_name
                                end
                              }
                            end
                            result
                          end
                          

                          从目录和所有子目录返回文件的相对路径

                          【讨论】:

                            【解决方案19】:

                            在 IRB 上下文中,您可以使用以下命令获取当前目录中的文件:

                            file_names = `ls`.split("\n")
                            

                            您也可以在其他目录上进行此操作:

                            file_names = `ls ~/Documents`.split("\n")
                            

                            【讨论】:

                            • 这个解决方案对我有用,因为我有一个旧的 ruby​​ 版本不支持 Dir.children 命令的旧解决方案
                            【解决方案20】:

                            如果您创建带有空格的目录:

                            mkdir "a b"
                            touch "a b/c"
                            

                            你不需要转义目录名,它会自动转义:

                            p Dir["a b/*"] # => ["a b/c"]
                            

                            【讨论】: