ぎょーぼのぶろぐ

IT系の話を書いていくブログです。今はRubyの勉強中。

【Ruby2.6】__FILE__ について考える。

前置き

Ruby の擬似変数__FILE__について、色々考えます。

そもそも、__FILE__ って何?

__FILE__は、「擬似変数」と呼ばれるものの一種です。Webで公開されているリファレンスマニュアルによると・・・

__FILE__
現在のソースファイル名
フルパスとは限らないため、フルパスが必要な場合は File.expand_path(__FILE__) とする必要があります。

となっています。実際、コード上で p __FILE__ で出力してみると、ファイル名のみだったり、フルパスだったり、中途半端に相対パスだったりと、さまざまな値が返ってきます。。。

Windows 環境のRubyで、以下のように確認してみました。

テスト用のファイル「C:\gyobo\test\a.rb」を作ります。内容は以下の一文のみ。

p __FILE__

これを、色々な方法で実行してみました。

C:\gyobo\test> ruby a.rb                # => "a.rb"
C:\gyobo> ruby test\a.rb                # => "test\a.rb"
C:\gyobo> ruby C:\gyobo\test\a.rb       # => "C:\gyobo\test\a.rb"
C:\gyobo> ruby c:\gyobo\test\a.rb       # => "c:\gyobo\test\a.rb"       あれ?
C:\gyobo> ruby ../gyobo\test\..\test\a.rb     # => "../gyobo/test/../test/a.rb"     そういうこと?

4行目、たまたまドライブ文字を小文字にしてみたら、__FILE__ の中身も小文字になったので、まさか・・・と思って5行目でやんちゃなことをしてみたのですが、、、

結局のところ、ruby コマンドで渡された実行ファイルへのパス、引数に指定したパスの文字列がそのまんま、__FILE__ に入るということのようです。

requireload で呼び出した場合

Ruby を実行開始したファイルではなく、requireload で呼び出した先では、__FILE__ の値はどうなるか? 先ほどの「a.rb」と同じディレクトリに、「req.rb」というファイルを作り、下の一行だけ書きます。

require './a.rb'

実行すると・・・

C:\gyobo\test>ruby req.rb
"C:/gyobo/test/a.rb"

相対パスで指定していますが、__FILE__ には絶対パスが格納されています。これは、require だけでなく、require_relativeload で読み込んでも同じです。また、カレントディレクトリを変更して実行した場合も同様*1require で指定する相対パスで探索し、見つかったらその絶対パスが入ってくるようです。

__FILE__ で得られるもの

まとめると、以下の通りのようです。

  • __FILE__ には、そのファイルを呼び出した際のパス文字列が格納される。
    1. ruby [ファイル名] で呼び出した場合は、「カレントディレクトリからの相対パス」文字列が格納される。
    2. require 等で呼び出した場合は、「絶対パス」文字列が格納される。

ポイントは、「文字列」である、ということです。

__FILE__.class         # => String

ファイル/ディレクトリオブジェクトではないので、格納されている文字列以上の情報は入っていません。上記1 の場合、__FILE__ の中身は相対パスの文字列でしかないので、__FILE__ だけでは、絶対パスを生成することはできません。ですので、絶対パスを取得する場合は、最初の引用の中にある通り、

フルパスとは限らないため、フルパスが必要な場合は File.expand_path(__FILE__) とする必要があります。

カレントディレクトリを基準に、絶対パスに展開できる File.expand_path 等を使って生成する必要があるわけです。

続きます。

*1:カレントディレクトリに合わせて、require で指定するパスを変更する必要があります