在 Ruby 中,如何解释(扩展)相对于目录的 glob?

In Ruby, how can I interpret (expand) a glob relative to a directory?

更广泛的上下文:区分大小写的文件系统上的不区分大小写的文件名

给定目录路径(作为字符串,可能相对于当前工作目录或绝对目录),我想打开一个特定文件。我知道文件的文件名,除了它的大小写。 (可能是 TASKDATA.XMLTaskData.xml 甚至 tAsKdAtA.xMl。)

受到 the accepted answer to Open a file case-insensitively in Ruby under Linux 的启发,我想出了这个小模块来生成一个 glob 来匹配文件名:

module Utils
  def self.case_insensitive_glob_string(string)
    string.each_char.map do |c|
      cased = c.upcase != c.downcase
      cased ? "[#{c.upcase}#{c.downcase}]" : c
    end.join
  end
end

对于我的具体情况,我将其称为

 Utils.case_insensitive_glob_string('taskdata.xml')

并且会得到

'[Tt][Aa][Ss][Kk][Dd][Aa][Tt][Aa].[Xx][Mm][Ll]'

具体上下文:glob 相对于 dir ≠ pwd

现在我必须扩展 glob,即将它与给定目录中的实际文件进行匹配。不幸的是,Dir.glob(...) 似乎没有参数来传递应该扩展 glob 的目录(路径)。直觉上,创建一个 Dir 对象并让它处理 glob 对我来说很有意义:

d = Dir.new(directory_path)
# => #<Dir:/the/directory>

filename = d.glob(Utils.case_insensitive_glob_string('taskdata.xml')).first() # I wish ...
# NoMethodError: undefined method `glob' for #<Dir:/the/directory>

... 但是 glob 仅作为 class 方法存在,而不是实例方法。 (任何人都知道为什么 Dir 的许多方法对 相对 特定 目录完全有意义?

看来我有两个选择:

  1. 将当前工作目录更改为给定目录

  2. 结合目录路径扩展文件名的 glob

第一个选项很简单:使用 Dir.chdir。但是因为这是在Gem,我不想弄乱我Gem的用户环境,所以我回避了它。 (与块概要一起使用可能比我完成后手动(或不)重置工作目录要好一些。)

第二个选项看起来简单。简单地做

taskdata_xml_name_glob = Utils.case_insensitive_glob_string('taskdata.xml')
taskdata_xml_path_glob = File.join(directory_path, taskdata_xml_name_glob)
filename = Dir.glob(taskdata_xml_path_glob).first()

,对吧?几乎。当 directory_path 包含在 glob 中具有特殊含义的字符时,当我只想对文件名进行 glob 扩展时,它们将 错误地扩展 。这不太可能,但由于路径是由 Gem 用户提供的,无论如何我都必须考虑它。

问题

我应该在 File.join 使用文件名 glob 之前转义 directory_path 吗?如果是这样,是否有工具可以做到这一点,或者我必须自己编写转义函数的代码吗?

或者我应该使用不同的方法(是 chdir,还是其他不同的方法)?

如果我要实现该行为,我会过滤一个由 Dir#entries:

返回的数组
Dir.entries("#{target}").select { |f| f =~ /\A#{filename}\z/i }

请注意,在 unix 平台上,... 条目 也会被列出 ,但它们不太可能在第二步。另外,文件名可能应该用 Regexp.escape:

转义
Dir.entries("#{target}").select { |f| f =~ /\A#{Regexp.escape(filename)}\z/i }