15

I have a Ruby script with path /foo/bar/gazook/script.rb. I also created a symlink to it in $HOME/bin.

Now, I want my Ruby script to access some other file in directory /foo, and to keep paths relative, I have a variable FOO_DIRECTORY = File.expand_path(File.dirname(__FILE__) + "/../../") in my script.

The problem is that if I run my script from its symlink, this relative directory is wrong (since I guess its expanding from a different location).

How do I fix this? Is there a way besides using an absolute path?

grautur
  • 29,955
  • 34
  • 93
  • 128

4 Answers4

15

You can use File.readlink to resolve a symlink but you'll want to check File.symlink? first.

path = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__

Then you can work with path instead of __FILE__. You might want to use $0 instead of __FILE__ as well, __FILE__ is the current filename whereas $0 is the name of the current script.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 6
    For a more general solution, you need to resolve symlinks in a loop, because one symlink may point to another. Plus, you have to check each and every part of the path: `bin` may be a symlink, too. I'd recommend `File.realpath`. – hagello Apr 07 '15 at 14:37
7

To get any path relative to the location of your script, always use __dir__.

__dir__ is a concise way of saying File.dirname(File.realpath(__FILE__)). It's available in Ruby >= 2.0. On __dir__.

File.realpath(__FILE__) (or Pathname#realpath) has three advantages compared to File.readlink:

  • It expands symlinks anywhere in the path. readlink only expands paths that are the last part of the argument.
  • It (recursively) resolves symlinks to symlinks to... readlink resolves only the first level.
  • You do not have to check whether path is a symlink at all. Thus you can drop the if File.symlink?.

Consequently it would be good to use FOO_DIRECTORY = File.join(__dir__, '..', '..') or FOO_DIRECTORY = File.dirname(File.dirname(__dir__))

hagello
  • 2,843
  • 2
  • 27
  • 37
2

Try this

require 'pathname'
p File.dirname(Pathname.new(__FILE__).realpath)
neoneye
  • 50,398
  • 25
  • 166
  • 151
-2

Try this:

FOO_DIRECTORY = File.expand_path("../../../", __FILE__)

I's say the problem is that your symlink file is interpreting File.dirname(__FILE__)

Marek Příhoda
  • 11,108
  • 3
  • 39
  • 53
  • `expand_path` doesn't resolve symlinks and specify the `dir_string` won't help: ["Relative paths are referenced from the current working directory of the process unless dir_string is given, in which case it will be used as the starting point."](http://ruby-doc.org/core-1.9.3/File.html#method-c-expand_path). Furthermore, the second argument to `expand_path` should be a *directory*, not a file name. – mu is too short Dec 16 '11 at 20:33
  • OK, I created a test file, a symlink in my ~/bin directory, and when running the symlink, I get "/home". So I apologize, my solution does not work :( – Marek Příhoda Dec 16 '11 at 21:31
  • And also notice that `File.expand_path("..", __FILE__)` is just a funny way of saying `File.dirname(__FILE__)`. – mu is too short Dec 16 '11 at 21:33