Techioz Blog

Rubyでの相対パスによるファイル参照

概要

CTFマシンが完成したのですが、なぜこれが機能するのか私の脳が理解するのが難しいので、わかるまで眠れないので質問させていただきました。私の質問は、相対パスの使用に関するものです。

以下は、dependency.yml ファイルを読み取るために相対パスを使用する list_from_file 関数を含む Ruby スクリプトのソース コードです。

henry@precious:~$ cat /opt/update_dependencies.rb
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'

# TODO: update versions automatically
def update_gems()
end

def list_from_file
  YAML.load(File.read("dependencies.yml"))
end

def list_local_gems
  Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end

gems_file = list_from_file
gems_local = list_local_gems

gems_file.each do |file_name, file_version|
  gems_local.each do |local_name, local_version|
    if(file_name == local_name)
      if(file_version != local_version)
        puts "Installed version differs from the one specified in file: " + local_name
      else
        puts "Installed version is equals to the one specified in file: " + local_name
      end
    end
  end
end

私の限られた知識では、このスクリプトを実行すると、スクリプトと同じディレクトリ (この場合は /opt/) 内にある dependency.yml ファイルのみが検索されると推測されます。

しかし、これは真実ではありません。 /tmp ディレクトリ内にこのファイルを作成し、スクリプトを実行すると、実際にそれが取得されます。

相対パスと絶対パスに関するいくつかの記事や他の stackoverflow の投稿を検索して読みましたが、実際にこれが発生する理由を説明するものはありませんでした。

密接に関連する投稿を見逃していたら、あらかじめごめんなさい。

解決策

Ruby の相対パスは常に現在の作業ディレクトリに依存します。現在の作業ディレクトリは、スクリプトが存在するディレクトリと必ずしも同じではなく、Ruby を起動した時点のディレクトリに存在します。

例:

> cd /tmp
> ruby /some/path/somewhere/to/my/script.rb

この場合、script.rb の現在の作業ディレクトリはどこになるでしょうか?

答え: /tmp です。これは、スクリプトを起動したときにいたディレクトリだからです。

スクリプト自体から現在の作業ディレクトリを表示するには、puts Dir.pwd という行を追加すると、その時点でスクリプトが実行されているフォルダーが出力されます。

スクリプトの現在の作業ディレクトリを変更するには、Dir.chdir を使用できます。

相対パスの使用が混乱を招きやすい理由がわかります。これはすべて、Ruby 実行可能ファイルの起動元のディレクトリに依存するためです。それはどこにでもあるかもしれません。

これが、ほとんどのスクリプトが絶対パス、またはスクリプト ファイル自体に対する相対パスのいずれかを使用する理由です。スクリプト ファイル自体に対する相対パスを使用するには、dir を開始点として使用し、そこからスクリプトの相対パスを構築します。

さらに詳しい議論はこちらをご覧ください: Ruby スクリプトは、それがどのディレクトリにあるかを認識できますか?