【问题标题】:Recursively scan specific folders for a file in ruby递归扫描特定文件夹以查找 ruby​​ 中的文件
【发布时间】:2014-03-28 10:01:40
【问题描述】:

我正在尝试递归扫描特定文件夹并搜索特定文件。

在根文件夹(例如,C:\Users\Me)中,我想只扫描名为 my* 的文件夹(因此,以字母 'my' + 开头的文件夹),然后查看是否存在是文件 .txt 并将第一行存储在变量中。

对于扫描,我正在尝试此代码,但没有成功

require 'find'

pdf_file_paths = []
path_to_search = ['C:\Users\Me'];

Find.find('path_to_search') do |path|
    if path =~ /.*\.txt$/
        #OPEN FILE
end

【问题讨论】:

  • 澄清一下,您要查看 'C:\Users\Me\not_my_folder\myfolder' 路径吗?

标签: ruby recursion directory


【解决方案1】:

我会这样做:

first_lines_of_each_file = []
Dir.glob("C:/Users/Me/**/my**/*.txt",File::FNM_CASEFOLD) do |filepath|
  File.open(filepath,'rb') { |file| first_lines_of_each_file << file.gets }
end

File::FNM_CASEFOLD 常量将使用 不区分大小写 搜索来搜索所有目录和文件。但是如果你想要区分大小写的搜索,那么不需要使用第二个参数File::FNM_CASEFOLD

如果您将目录组织为

C:/Users/Me/
|- my_dir1/
    |- a.txt
    |- my_dir2/
        |- foo.txt
        |- baz.doc
|- my_dir3/
    |- biz.txt
  • Dir.glob("C:/Users/Me/**/my**/*.txt" 将为您提供所有 .txt 文件。因为这里的搜索是递归的。

  • Dir.glob("C:/Users/Me/my**/*.txt" 只会为您提供位于目录中的 .txt 文件,它们是C:/Users/Me/ 的直接子级。您将获得的文件只有 a.txtbiz.txt

【讨论】:

  • my** 需要两颗星吗?不幸的是,它不会进行选择性递归
  • @BroiSatse 是的。** 匹配目录,* 匹配文件。
  • @BroiSatse 确实如此。当我在我的机器上运行此代码时,它按预期工作。
  • 刚刚尝试运行Dir['&lt;my_rails_root&gt;/a**/docs']。它应该给我&lt;my_rails_root&gt;/app/assets/docs 文件夹路径 - 它没有。
  • 是的 - 使用 Dir['&lt;my_rails_root&gt;/a*/a*/docs'] 运行时返回。似乎* 只是一个匹配任何内容的占位符,无论是文件夹还是文件都没有关系。 ** 是一个特殊的位,它递归地匹配所有文件夹,但如果与其他字符一起放置,它就不起作用。上面的代码将匹配所有以 my 开头的文件夹,但它们也可能在非我的文件夹中进行匹配(C:/User/Me/foo/my-foo/ 将匹配)
【解决方案2】:

这应该可以完成工作:

lines = Dir.glob("#{path}/**/my*/*.txt").map do |filename|
  File.open(filename) do |f|
    f.gets
  end
end

Dir.glob 类似于 *nix 机器上的 glob 可执行文件。这也适用于 Windows。 gets 获取第一行。确保您使用forward slash even for a Windows machine

【讨论】:

  • 啊!抱歉,错过了my*
  • File.open(filename).gets 是不可接受的......因为没有文件,你正在关闭......
  • 我希望open 能够解决这个问题:/ 源代码表明只有在传入块时才会自动关闭。我的错误。
  • 这不会进行递归搜索。
  • @BroiSatse OP 没有明确提到,如果他/她想递归搜索任何以my 开头的文件夹,从C:/Users/Me/ 开始。如果 OP 想要递归,那么 ** 需要,否则不需要。
【解决方案3】:

我不确定这是否是最干净的解决方案,但您可以尝试:

def find_files(file_name, root_path, folder_pattern = nil)
  root_path = File.join(root_path, '')
  paths = Dir[File.join(root_path, '**', file_name)]
  paths.keep_if! {|p| p.slice(path.size, p.size).split('/').all? {|s| s =~ folder_pattern}} if folder_pattern
end

find_files('C:/Users/Me', 'find_me.txt', /my.*/)

【讨论】: