argv[0]
references the shell command that caused the current process to load. getcwd()
returns current working directory. From that info, can I construct an absolute path to the program's binary executable file? I don't expect it to work if the program has done a cd
since it was first loaded.
2 Answers
In the internals of a programming language interpreter, I use the following code to get the self path.
This has special code for Linux, Windows (including Cygwin), Mac and Solaris:
#if __APPLE__ #include <mach-o/dyld.h> #endif #if __linux__ static val get_self_path(void) { char self[PATH_MAX] = { 0 }; int nchar = readlink("/proc/self/exe", self, sizeof self); if (nchar < 0 || nchar >= convert(int, sizeof self)) return nil; return string_utf8(self); } #elif HAVE_WINDOWS_H static val get_self_path(void) { wchar_t self[MAX_PATH] = { 0 }; DWORD nchar; SetLastError(0); nchar = GetModuleFileNameW(NULL, self, MAX_PATH); if (nchar == 0 || (nchar == MAX_PATH && ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) || (self[MAX_PATH - 1] != 0)))) return nil; return string(self); } #elif __APPLE__ static val get_self_path(void) { char self[PATH_MAX] = { 0 }; uint32_t size = sizeof self; if (_NSGetExecutablePath(self, &size) != 0) return nil; return string_utf8(self); } #elif HAVE_GETEXECNAME static val get_self_path(void) { val execname = string_utf8(getexecname()); if (car(execname) == chr('/')) return execname; return scat3(getcwd_wrap(), chr('/'), execname); } #else static val get_self_path(void) { char self[PATH_MAX]; if (argv[0] && realpath(argv[0], self)) return string_utf8(self); return lit(HARD_INSTALLATION_PATH); } #endif
The val
type, nil
symbol, and the functions string
and string_utf8
are internal to the interpreter. convert
is just a macro that abstracts casting. Of course, some header files are required for readlink
, realpath
and so on.
The getexecname
function is something on Solaris. It seems that for some reason I coded in a case for handling the path being relative, in which case the current directory has to be stuck onto it to get it absolute.
The fallback strategy is that if the argv[0]
from main
is avilable, then call realpath
on it and run with it. If argv[0]
is unavailable then we rely on a hard-coded installation path, which comes from a Makefile
variable. For instance if the program is installed as /usr/bin/foo
, then the Makefile
recipe inserts this on the compiler command line as -DHARD_INSTALLATION_PATH=/usr/bin/foo
. This will, of course, only be correct if the program is installed at the path it was built for.
- The code here is extremely poor quality. Don't use boilerplate macros and functions in code on stack exchange especially when not including said macros and functions.– qz-CommentedJun 26, 2023 at 1:33
On Linux you can use the symlink /proc/self/exe
$ ls -l /proc/self/exe lrwxrwxrwx 1 rudi users 0 2016-10-18 16:20 /proc/self/exe -> /usr/bin/ls
argv[0]
could be anything, see unix.stackexchange.com/a/315819/64248.argv[0]
could be anything, you can make it a documented requirement for your program that certain features of it (perhaps all of them) require it to calculate its own path, which (at least on some platforms) requiresargv[0]
to be non-null, and with a sane value: the path, perhaps relative to cwd, by which it was invoked. Any program can be misused contrary to its documented correct use, with invalid arguments or other situations.