3

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);
}
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • If you can alter the script, why not using `env` invoker to detect if the shell script is 'shebang invoked' or 'interpreter loaded' ? Something like a `#!/usr/bin/env -S SHEBANG=1 "shell"` shebang ? – Zilog80 Jul 15 '21 at 16:12
  • @Zilog80 We cannot alter shebangs in users' Python scripts. See the linked question. – ivan_pozdeev Jul 15 '21 at 19:34

1 Answers1

-2

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.

jdevries3133
  • 135
  • 1
  • 6
  • We need to do the detection in the `python` executable substitute (which is currently written in Bash -- but replacing it with a compiled program is acceptable), not in Python code. – ivan_pozdeev Jul 16 '21 at 10:02
  • Plus, the first command line argument _is always_ `python*` in the post-shebang command line. – ivan_pozdeev Jul 16 '21 at 10:05