解释器如何检测是从脚本而不是命令行调用的?
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
执行:
-arg1 val1 -arg2 val2
my-script-file
(并且不是,正如人们可能天真地期望的那样,有 5 个参数
-arg1
val1
-arg2
val2
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
函数。并且肯定存在效果不佳的情况。
作为 "is known",以
开头的脚本my-script-file
#!/path/to/interpreter -arg1 val1 -arg2 val2
由 exec
使用 2(!) 参数调用 /path/to/interpreter
执行:
-arg1 val1 -arg2 val2
my-script-file
(并且不是,正如人们可能天真地期望的那样,有 5 个参数
-arg1
val1
-arg2
val2
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
函数。并且肯定存在效果不佳的情况。