How to get the current running binary root realpath from Crystal source code

Assume i built my binary from ~/foo/ use shards build, i will get a binary like this: ~/foo/bin/foo, then i copy foo into ~/utils/bin/, so, the realpath of foo should be ~/utils/bin/foo.

So, i there a way to get the above ~/utils/bin/foo from Crystal source dynamically when running foo?

For my use case, i want to search a SQLite db file, it should be created on the same folder as foo binary, thanks.

This should get you the absolute path. Remove expand if you don’t need it to be absolute.

Path[PROGRAM_NAME].expand.dirname

Hi, It’s seem like not work when run binary directly in the $PATH, following is a example, my binary name is 1, and this file copy to ~/utils/bin which is added in the $PATH.

 ╰─ $ pwd
/home/zw963

 ╰─ $ 1
Path[PROGRAM_NAME].expand.dirname # => "/home/zw963"

 ╰─ $ which -a 1
/home/zw963/utils/bin/1
1 Like

And this is why I shouldn’t answer questions 10 minutes after waking up :laughing:

Here’s a few other ways that I tested on my system.

# Method 1: Check the /proc filesystem if the OS has one.
puts File.readlink("/proc/self/exe") # Linux
#puts File.readlink("/proc/curproc/file") # FreeBSD

# Method 2: use PROGRAM_NAME or $PATH
argv0 = Path[PROGRAM_NAME]
if argv0.absolute?
  # Can use argv0
  puts "From absolute: #{argv0}"
elsif argv0.to_s.includes?('/')
  # Relative path, expand it
  puts "From relative: #{argv0.expand}"
else
  # Search PATH
  puts "Found in path?: #{Process.find_executable(argv0.basename)}"
end

Just take the Path.dirname as-needed.

Thanks, i learn a lot from your answer.

The method 1 works on my Arch linux, but considering possible compatibility, not sure if File.readlink("/proc/curproc/file") works on mac OS, i select the method 2.

1 Like

Process.executable_path

1 Like

Wow, Works like a charm!

As the document said:

Returns nil if the file can’t be found.

When the file can’t be found and Process.executable_path return nil?

If the executable file doesn’t exist (anymore).

Example:

Process.executable_path.try{|path| File.delete(path) }
Process.executable_path # => nil
1 Like