解释器如何检测是从脚本而不是命令行调用的?

How can interpreter detect being called from a script as opposed to command line?

作为 "is known",以

开头的脚本 my-script-file
#!/path/to/interpreter -arg1 val1 -arg2 val2

exec 使用 2(!) 参数调用 /path/to/interpreter 执行:

  1. -arg1 val1 -arg2 val2
  2. my-script-file

(并且不是,正如人们可能天真地期望的那样,有 5 个参数

  1. -arg1
  2. val1
  3. -arg2
  4. val2
  5. my-script-file

正如之前许多问题中所解释的那样,例如, ).

我的问题来自解释器开发人员的视角,不是脚本编写者

如何从 interpreter 可执行文件中检测到我是从 shebang 而非命令行调用的?

然后我就可以决定是否需要拆分我的第一个参数 通过 space 是否从 "-arg1 val1 -arg2 val2" 变为 ["-arg1", "val1", "-arg2", "val2"]

这里的主要问题是其中以 space 命名的脚本文件。 如果我总是拆分第一个参数,我会像这样失败:

$ my-interpreter "weird file name with spaces"
my-interpreter: "weird": No such file or directory

在Linux上,使用GNU libc或musl libc,您可以使用aux-vector来区分这两种情况。

下面是一些示例代码:

#define _GNU_SOURCE 1
#include <stdio.h>
#include <errno.h>
#include <sys/auxv.h>
#include <sys/stat.h>

int
main (int argc, char* argv[])
{
  printf ("argv[0] = %s\n", argv[0]);
  /* https://www.gnu.org/software/libc/manual/html_node/Error-Messages.html */
  printf ("program_invocation_name = %s\n", program_invocation_name);
  /* http://man7.org/linux/man-pages/man3/getauxval.3.html */
  printf ("auxv[AT_EXECFN] = %s\n", (const char *) getauxval (AT_EXECFN));
  /* Determine whether the last two are the same. */
  struct stat statbuf1, statbuf2;
  if (stat (program_invocation_name, &statbuf1) >= 0
      && stat ((const char *) getauxval (AT_EXECFN), &statbuf2) >= 0)
    printf ("same? %d\n", statbuf1.st_dev == statbuf2.st_dev && statbuf1.st_ino == statbuf2.st_ino);
}

直接调用的结果:

$ ./a.out 
argv[0] = ./a.out
program_invocation_name = ./a.out
auxv[AT_EXECFN] = ./a.out
same? 1

通过以 #!/home/bruno/a.out:

开头的脚本调用的结果
$ ./a.script 
argv[0] = /home/bruno/a.out
program_invocation_name = /home/bruno/a.out
auxv[AT_EXECFN] = ./a.script
same? 0

当然,这种方法非常不可移植:只有 Linux 具有 getauxv 函数。并且肯定存在效果不佳的情况。