在 Ruby 中,如何解释(扩展)相对于目录的 glob?
In Ruby, how can I interpret (expand) a glob relative to a directory?
更广泛的上下文:区分大小写的文件系统上的不区分大小写的文件名
给定目录路径(作为字符串,可能相对于当前工作目录或绝对目录),我想打开一个特定文件。我知道文件的文件名,除了它的大小写。 (可能是 TASKDATA.XML
、TaskData.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
的许多方法对 相对 与 特定 目录完全有意义?
看来我有两个选择:
将当前工作目录更改为给定目录
或
- 结合目录路径扩展文件名的 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 }
更广泛的上下文:区分大小写的文件系统上的不区分大小写的文件名
给定目录路径(作为字符串,可能相对于当前工作目录或绝对目录),我想打开一个特定文件。我知道文件的文件名,除了它的大小写。 (可能是 TASKDATA.XML
、TaskData.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
的许多方法对 相对 与 特定 目录完全有意义?
看来我有两个选择:
将当前工作目录更改为给定目录
或
- 结合目录路径扩展文件名的 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 }