LoginSignup
2
1

More than 3 years have passed since last update.

Pathname#globをFile::FNM_DOTMATCHオプション付きで呼びだしたときに、”.”や”..”が消えてしまうのはなぜ?

Last updated at Posted at 2020-05-10

疑問に思った点

Dir.globPathname.globでワイルドカード(*)を指定し、なおかつFile::FNM_DOTMATCHオプションを渡すと、...が含まれるパスが返ってきます。

require 'pathname'

pathname = Pathname.pwd
#=> #<Pathname:/Users/jnito/dev>

# Dir.glob は "." と ".." を保持する
Dir.glob(pathname.join('*'), File::FNM_DOTMATCH).sort[0..1]
#=> ["/Users/jnito/dev/.", "/Users/jnito/dev/.."]

# Pathname.glob も "." と ".." を保持する
Pathname.glob(pathname.join('*'), File::FNM_DOTMATCH).sort[0..1]
#=> [#<Pathname:/Users/jnito/dev/.>, #<Pathname:/Users/jnito/dev/..>]

しかし、Pathname#glob(インスタンスメソッドのglob)を使うと...が消えて、カレントディレクトリや親ディレクトリそのものが返ってきます。

require 'pathname'

pathname = Pathname.pwd
#=> #<Pathname:/Users/jnito/dev>

# Dir.globやPathname.globと異なり、インスタンスメソッドのglobは"."や".."ではなく、カレントディレクトリや親ディレクトリが返ってくる
pathname.glob('*', File::FNM_DOTMATCH).sort[0..1]
#=> [#<Pathname:/Users/jnito>, #<Pathname:/Users/jnito/dev>]

僕としてはクラスメソッドのPathname.globを呼びだしたときと同じように#<Pathname:/Users/jnito/dev/.>#<Pathname:/Users/jnito/dev/..>が返ってきてほしかったのですが、この挙動の違いはどういう理由で発生するのでしょうか?その理由を以下にまとめます。

結果が異なる理由

Pathname.globDir.globの結果をそのままPathnameオブジェクトに変換します。イメージとしては以下のような感じです。(実際はCで実装されています)

class Pathname
  # クラスメソッドのglobの実装イメージ
  def self.glob(pattern, flags=0)
    Dir.glob(pattern, flags).map do |path|
      Pathname(path)
    end
  end
end

一方、インスタンスメソッドのPathname#globDir.globbaseオプションに自分自身のパスを設定し、Dir.globの結果と自分自身を表すパスを+で結合します。イメージとしては以下のような感じです。(こちらも実際はCで実装されています)

class Pathname
  # インスタンスメソッドのglobの実装イメージ
  def glob(pattern, flags=0)
    Dir.glob(pattern, flags, base: self).map do |path|
      self + path
    end
  end
end

ポイントとなるのはself + pathの部分です。

baseオプションを指定すると、Dir.glob...という文字列を返します(=baseからの相対パスを返す)。そしてこれらの文字列をPathnameオブジェクトに+で結合すると、...が消えて、カレントディレクトリや親ディレクトリのパスが返ります。

pathname = Pathname.pwd
#=> #<Pathname:/Users/jnito/dev>

# "."を結合すると、"."が消えてカレントディレクトリのパスになる
pathname + '.'
#=> #<Pathname:/Users/jnito/dev>

# ".."を結合すると、".."が消えて親ディレクトリのパスになる
pathname + '..'
#=> #<Pathname:/Users/jnito>

# "."や".."でなければ、素直にパスが結合される
pathname + 'foo'
#=> #<Pathname:/Users/jnito/dev/foo>

インスタンスメソッドのPathname#globは内部的に以下のような処理が行われているため、File::FNM_DOTMATCHオプションを渡しても...が保持されず、カレントディレクトリや親ディレクトリが返ってくる、というわけです。

参考文献

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1