How to parse full path apart from filename?

I can get the relative path and filename of the executable with:

print "Relative path and filename = ", PROGRAM_NAME, "\n"

And I can get the full path and filename of the executable with:

print "Full path, including filename = ", Path[PROGRAM_NAME].expand, "\n"

And I can get just the filename of the executable with:

print "Filename = ", Path[PROGRAM_NAME].basename, "\n"

But without doing string parsing, I can not for the life of me figure out how to get just the path, without the filename of the executable.

Surely itā€™s only because Iā€™m not enough of a programmer to comprehend the documentation?

Anyone know the magic incantation I need?

Thanks!

SOLVED.

print "Full path, excluding filename = ", Path[PROGRAM_NAME].expand.dirname, "\n"

Thanks!

Path#dirname is correct to get the path of the parent directory.

However, Path[PROGRAM_NAME].expand only works correctly when the program is executed via a relative path.
PROGRAM_NAME is whatever the user typed into their shell to run the program. If itā€™s a path relative to the current directory, Path#expand works fine (and also if itā€™s an absolute path, of course).
If however itā€™s only a name thatā€™s resolved in $PATH, this mechanism would be broken.

You might want to use Process.executable_path instead.

I appreciate your response, but I fear you expect more programming/Crystal knowledge from me than I possess. This code:

puts Path#dirname

just outputs the word ā€œPathā€ for me, and this code:

puts Process.executable_path

outputs the entire path with filename, and this path:

puts Process.executable_path.dirname

generates an error from the compiler:

In first_steps.cr:8:30

8 | puts Process.executable_path.dirname
^------
Error: undefined method ā€˜dirnameā€™ for Nil (compile-time type is (String | Nil))

Reference the official doc

Returns an absolute path to the executable file of the currently running program. This is in opposition to PROGRAM_NAME which may be a relative or absolute path, just the executable file name or a symlink.

The executable path will be canonicalized (all symlinks and relative paths will be expanded).

Returns nil if the file canā€™t be found.

Although, i really donā€™t know why THE fILE CAN'T BE FOUND in this case, maybe core member can give some detailed explain.

The workaround is: puts Process.executable_path.as(String).dirname

Path#dirname is just a convention to colloquially reference a method. Itā€™s not intended as source code.

A simple reason for this would be that the executable file which spawned the current process has since been removed.
For example demonstrated by the following program:

if path = Process.executable_path
  File.delete(path)
end

Process.executable_path # => nil

This is just exposing a feature of the operating system and there might be all kinds of other reasons why the OS cannot report the path name or why itā€™s not available to the current process.

1 Like