如何搜索一个文件夹及其所有子文件夹中特定类型的文件

77

我正在尝试在指定文件夹中搜索所有给定类型的文件并将它们复制到新文件夹。

我需要指定一个根文件夹,并在该文件夹及其所有子文件夹中搜索符合给定类型的任何文件。

如何搜索根文件夹的子文件夹及其子文件夹? 似乎递归方法可以解决问题,但我无法正确实现它。

5个回答

124

试试这个:

Dir.glob("#{folder}/**/*.pdf")

这与

Dir["#{folder}/**/*.pdf"]

其中folder变量是你想要搜索的根文件夹的路径。


3
我认为原帖作者想要实现递归,不是吗? - rogerdpack
2
据我所知,这个方法是递归的。实际上,答案应该是 Dir.glob("#{folder}/**/*.pdf"),其中变量 folder 是您要搜索的根文件夹的路径。 - Automatico
1
默认情况下不区分大小写 - leifg
2
@Konstantin 这个或者 Dir#[] 是我通常使用的方法。但是有一个问题:Dir.glob 会将所有路径加载到内存中。这通常没有问题,但如果您有大量路径,最好使用 Find 模块,因为它在发现路径时会将其传递给块。 - Wayne Conrad
2
我同意@WayneConrad的观点。当Ruby分配足够的内存来存储一个大数组时,您可能会无意中停止程序。这与 slurping a file 非常相似。让Find处理层次结构比将其抛给操作系统并潜在地获得意外数组更有效,也可能更快。调试这种情况很困难。 - the Tin Man
显示剩余3条评论

65

你需要使用Find模块。 Find.find 接受一个包含路径的字符串作为参数,然后将父目录路径和每个文件和子目录的路径传递给相应的代码块。以下是一些示例代码:

require 'find'

pdf_file_paths = []
Find.find('path/to/search') do |path|
  pdf_file_paths << path if path =~ /.*\.pdf$/
end

这将递归搜索一个路径,并将以 .pdf 结尾的所有文件名存储在一个数组中。


28
如果速度是一个问题,建议优先使用Dir.glob而不是Find.find
Warming up --------------------------------------
           Find.find   124.000  i/100ms
            Dir.glob   515.000  i/100ms
Calculating -------------------------------------
           Find.find      1.242k (± 4.7%) i/s -      6.200k in   5.001398s
            Dir.glob      5.249k (± 4.5%) i/s -     26.265k in   5.014632s

Comparison:
            Dir.glob:     5248.5 i/s
           Find.find:     1242.4 i/s - 4.22x slower

 

require 'find'
require 'benchmark/ips'

dir = '.'

Benchmark.ips do |x|
  x.report 'Find.find' do
    Find.find(dir).select { |f| f =~ /\*\.pdf/ }
  end

  x.report 'Dir.glob' do
    Dir.glob("#{dir}/**/*\.pdf")
  end

  x.compare!
end

使用 ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin15]


2
谢谢您的帖子。对于像我这样的初学者来说,它非常有帮助,可以弄清楚在 Dir.globFind.find 之间应该使用哪种方法。 - itsh
5
在这种情况下,使用正则表达式进行查找会更慢。而另一方面,Dir.glob 不如正则表达式强大,因此我预计它会更快。 - hirowatari
我想你可以使用 #end_with? 来更仔细地比较它们.... - rogerdpack
1
@hirowatari 正则表达式与否并没有区别 - 你可以用 false 替换整个块内容,它仍然会显著变慢(试一试吧)。这是因为调用块也需要一些时间,并且对于找到的每个项目都会发生,而 glob 过滤器在内部过滤并只在收集结果完成后返回。因此,您在使用 find 时使用的过滤器可以像您喜欢的那样复杂,它可以是具有查找和多个正则表达式的100行代码,而 glob 只理解每次调用一个简单的模式。如果您可以以这种方式表达您的搜索,请优先考虑使用 glob - Mecki
但是如果您需要实际处理这些文件,则需要为每个文件调用某些内容。因此,根据用例,比较可能不公平。此外,对于巨大的目录树,您可能不想在内存中存储整个数组。因此,有时一个方法会更好,另一次则使用另一种方法会更好。 - akostadinov

13
作为对 Jergason 和 Matt 上面答案的小改进,以下是如何将其简化成一行代码:
pdf_file_paths = Find.find('path/to/search').select { |p| /.*\.pdf$/ =~ p }

这里使用了与上述相同的“Find”方法,但利用了结果是可枚举的事实(因此我们可以使用“select”),以便获得一个包含匹配项集合的数组。


-1
另一种快速的方法是将任务委托给shell命令"find"并分割输出:
pdf_file_paths = `find #{dir} -name "*.pdf"`.split("\n")

无法在Windows上运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接