【Ruby2.6】__FILE__ を使ってできること
前置き
前回の続きです。
__FILE__
を使って色々な情報を取得してみます。
実行している「ファイル名のみ」を取得する
最後のパス区切り文字以降がファイル名になるので、それを取り出すメソッドを使う、正規表現で抽出したり、文字列加工したりすることで取得することができます。
下記、取り出し例です。C:\gyobo\test\a.rb
内で実行すると全て、"a.rb"文字列を返します。__FILE__
の中身が絶対パスか、相対パスかにかかわらず、同じ結果が返ります。
# File.basename を使う。 File.basename(__FILE__) # "/" で配列に分割後、配列要素の最後のものを取る。 __FILE__.split("/")[-1] # 最後の"/"から最後までを抽出。先頭に"/" が残るので、それを削除する。 # "/" が無い場合は、そのままの値を返す。 __FILE__.include?("/") ? __FILE__.match(/\/[^\/]*$/).to_s.delete("/") : __FILE__
素直に File.basename
を使うのが一番良いと思います。何かの理由でFile
オブジェクトが使えない場合は、split
を使うのが次善策でしょうか。
実行しているファイルの絶対パスを取得する
前回の記事でも書きましたが、__FILE__
に相対パスが入る場合、相対パスのベースとなるディレクトリの情報は入っていません。ですので、カレントディレクトリの情報と合わせる必要があります。
絶対パスを自力で生成しようとする場合、Dir.pwd
等でカレントディレクトリを取得し、join で結合することになりますが、__FILE__
が絶対パスかどうかの確認が必要になり、また__FILE__
の中に、親ディレクトリ..
やカレントディレクトリ.
等が入っている場合、ちゃんと処理をしないとそのまま入ってきてしまいます。
File.expand_path
を使えば、第一引数が絶対パスであれば、第二引数を指定しなければ、カレントディレクトリを基準に絶対パスへの展開を行ってくれます。また、..
や .
の処理も合わせて行ってくれるので、素直に File.expand_path
を使った方が良いです。
下記、そのまんまですが、取り出し例です。ファイルの絶対パスは、C:\gyobo\test\a.rb
です。
File.expand_path(__FILE__) # => "C:\gyobo\test\a.rb"
注意点ですが、絶対パスを取得する場合は、取得前にカレントディレクトリの変更(Dir.chdir
など)をしてしまうと、正しく取得できません。
p __FILE__ # => "a.rb" File.expand_path(__FILE__) # => "C:\gyobo\test\a.rb" Dir.chdir("C:\\") File.expand_path(__FILE__) # => "C:\a.rb" 移動したカレントディレクトリからのパスを生成してしまう。
実行しているファイルがあるディレクトリを取得する。
実行しているファイルの絶対パスを取得した後、絶対パスのディレクトリ部分を取得すればいいです。ですが、File.expand_path
だけを使う方法もあります。
# ファイルの絶対パスからディレクトリを取得する。 File.dirname(File.expand_path(__FILE__)) # => "C:\gyobo\test" # File.expand_path だけを使っても取れる。第一引数だけ注意。 p File.expand_path("..", __FILE__) # => "C:\gyobo\test"
File.expand_path
の方は、なんで第一引数が親ディレクトリ..
指定なのか?これは、File.expand_path
の特性を利用しています。
File.expand_path
メソッドは、「第一引数のパスを、第二引数のパスを基準にして絶対パスに展開」します。第二引数も相対パスの場合、カレントディレクトリを基準にして、第二引数、第一引数のパスを絶対パスに展開してくれます。
- 第二引数に
__FILE__
を指定した場合、まず第二引数がカレントディレクトリを基準にして展開され、C:\gyobo\test\a.rb
となります。 - ですが、第二引数にはディレクトリが来ることが想定されているので、このパスは
C:\gyobo\test\a.rb\
、つまりa.rb
はファイルではなくディレクトリである、と解釈されます。 - ですので、
a.rb
ディレクトリを基準にして一つ上のディレクトリが、ファイルのあるディレクトリとなるので、第一引数が親ディレクトリ..
の指定となります。
この特性を利用すると、実行しているファイルと同じディレクトリにあるlib
ディレクトリのパスを取得したい場合は、下記のようになります。
# 実行ファイルと同じディレクトリにある"lib"ディレクトリの絶対パスを取得する。 p File.expand_path("..\lib", __FILE__) # => "C:\gyobo\test\lib"
便利なのですが、見た目上、一階層ずれるので、一瞬間違っているように見えることが注意点です。また、カレントディレクトリを基準にして取得するので、前項と同様に、取得前にカレントディレクトリの変更(Dir.chdir
など)をしてしまうと、正しく取得できなくなります。
もう一つの方法として、__dir__
メソッドでも取得することができます。(ruby 2.0 以上?)
p __dir__ # => "C:\gyobo\test\"
こちらは、カレントディレクトリの変更をしても、元のディレクトリが正しく取得できるので、バージョンに問題がなければ、こちらを使う方がおすすめだと思います。
ただ、こちらも注意点があり、リファレンスマニュアルの__dir__
の項目 にもある通り、「シンボリックリンクを解決」したパスを返してきます。ですので、パスの中にシンボリックリンクが含まれている場合、File.expand_path
と __dir__
の結果が違ってきます。
例えば、実体ファイルが C:\gyobo\test\a.rb
で、C:\gyobo\test2\link_test
を C:\gyobo\test
へのシンボリックリンクとして作成して、C:\gyobo\test2\link_test\
をカレントディレクトリにして、a.rb を実行します。
p File.expand_path("..", __FILE__) # => "C:\gyobo\test2\link_test" p __dir__ # => "C:\gyobo\test"
これは、実行ファイルa.rb
自体をシンボリックリンクにした場合も同様です。
Windows環境の場合は、そもそもシンボリックリンクを作ること自体がけっこう特殊な状況なので、問題はより起きにくいと思いますが、linux系で、シンボリックリンク生成をする可能性がある場合は、要注意です。
まとめ
- 実行している「ファイル名のみ」を取得する
File.basename(__FILE__)
- 実行しているファイルの絶対パスを取得する
File.expand_path(__FILE__)
- 実行しているファイルのあるディレクトリの絶対パスを取得する
File.expand_path("..", __FILE__)
__dir__
注意点としては、2点です。