Get pre-shebang executable path in MacOS (equivalent to getauxval(AT_EXECFN) )

Viewed 227

For the problem described at bash - Detect if a script is being run via shebang or was specified as a command line argument - Unix & Linux Stack Exchange, we need to distinguish between cases when a script is run via shebang and as an argument to the interpreter.

An answer to that question suggests getting the pre-shebang executable name using getauxval(AT_EXECFN) -- which works, but only in Linux.

Since the Pyenv project also officially supports MacOS, we need an equivalent for that if we are to consider that solution.


I've checked Finding current executable's path without /proc/self/exe -- but both _dyld_get_image_name(0) and _NSGetExecutablePath give the post-shebang name. Here's a sample program that I used to do the checking (see the question link above on how it's used; its compilation result needs to be put in place of the python3 Bash script given in that question):

#include <stdio.h>
#include <unistd.h>
/*#include <sys/auxv.h>*/
#include <mach-o/dyld.h>
#include <sys/param.h>
#include <alloca.h>

int main(int argc, char** argv) {
        //char *at_execfn = (char*)getauxval(AT_EXECFN);
        //const char *at_execfn = _dyld_get_image_name(0);
        char *at_execfn = (char*)alloca(MAXPATHLEN);
        uint32_t at_execfn_len = MAXPATHLEN;
        _NSGetExecutablePath(at_execfn,&at_execfn_len);          
        printf("original executable: '%s'\n",at_execfn);
        for(int i=0; i<argc; i++) {
                printf("'%s'\n",argv[i]);
        }
        execvp("python3",argv);
}
1 Answers

This answer is based on the following assumptions; I'm sure others will vet whether they are true, but to my understanding, they are:

  1. Python scripts will only use the shebang if they are executed directly.
  2. Otherwise, the first command line argument will always be python, python3, or some other variation (python3.x, etc.).

You can already get the path to the original file, which is good because you can read what the shebang says, but you don't yet know whether the shebang was used, right? Python 3.10 offers an appealing solution: sys.orig_argv, which includes all the command line arguments, not just those from the program name forward as you get with normal sys.argv.

However, I'm sure you won't be implementing a 3.10-exclusive feature into pyenv! If that is the case, you can see the older C-API Py_GetArgcArgv, whose docs simply state:

Get the original command line arguments, before Python modified them.

Either way, I think that having the file path so you can read the shebang is the first part of the puzzle. The second part is figuring out if the shebang was actually used, and I think that the answer is in the command line arguments for most cases.

Related