为什么我们在 execlp 中双重声明可执行文件名称?
Why do we double state the executable name in `execlp`?
对于 运行 一个带有 execlp
的命令,我们执行
execlp("ps", "ps", NULL);
这里可以看出冗余,因为我们传递了两次 ps
。此行为在所有 exec
变体中都是一致的。
为什么 exec 需要这样的冗余?为什么不写成我们可以简单
execlp("ps", NULL);
第二个参数应该与第一个参数匹配,但不需要相同。
根据文档,
argv
is an array of argument strings passed to the new program. By convention, the first of these strings should contain the filename associated with the file being executed.
如果您有令人信服的理由对您计划 运行 的程序隐藏正在执行的文件的名称,您可以为第二个参数传递任何您想要的字符串,包括空的或一个 NULL
字符串:
execlp("ps", "<hidden>", NULL);
第一个参数是要执行的文件路径(/bin/ls
,/home/development/myproject/foo
)。其余参数对应于传递给 main
的 argv
向量。想象一下在 shell 中输入以下内容:
$ ./foo bar bletch
可执行路径是 ./foo
- 这是传递给 execlp
的第一个参数。按照惯例,argv[0]
应该是用于调用程序的字符串,因此完整的 argv
向量是 {"./foo", "bar", "bletch", NULL}
。因此,
execlp( "./foo", /* executable path */
"./foo", /* argv[0] */
"bar", /* argv[1] */
"bletch", /* argv[2] */
NULL /* argv[3] */
);
您可能不希望 argv[0]
与实际命令路径相同(比如因为路径是别名或者您不想公开确切的路径),在这种情况下您可以使用类似
的东西
execlp( "./secret/path/to/foo", "./foo", "bar", "bletch", NULL );
您不必为程序提供可执行文件的名称作为其 argv[0]
。你可以编写一个程序(rvw11.c
编译为 rvw11
可执行文件):
#include <unistd.h>
int main(void)
{
execlp("sleep", "rip van winkle", "40", (char *)0);
return 1;
}
你会发现如果你 运行:
$ ./rvw11 &
[1] 53034
$ ps
PID TTY TIME CMD
515 ttys000 0:00.07 -bash
534 ttys002 0:00.09 -bash
543 ttys003 0:01.15 -bash
558 ttys004 0:00.32 -bash
53034 ttys004 0:00.01 rip van winkle 40
$
因此,ps
列表中显示的名称是参数列表中作为 argv[0]
给出的值,而不是可执行文件的名称。 (在 macOS Sierra 10.12.2 上演示,但在大多数类 Unix 系统上你会得到类似的结果。)
这也说明了为什么从参数列表中确定可执行文件的名称不一定可行。
其他答案已经解释说您可以提供与程序名称不同的 argv[0]
,但没有解释 为什么 您可能想要这样做。
一些程序的行为因调用它们所用的名称而异。一个常见的例子是 shells,例如sh
、bash
和 csh
。他们检查 argv[0]
的第一个字符,如果这是 -
,他们将作为登录 shell 而不是常规的 shell。因此,当 /bin/login
调用用户登录 shell 时,它会执行以下操作:
execlp("/bin/bash", "-bash", (char*)NULL);
这样,bash
知道它是 运行 作为登录的一部分,并且可以相应地进行操作。这可以使用选项参数来完成,但是每个可能用作登录 shell 的程序都必须识别该选项(一些特殊的用户名有登录 shell,但实际上并不是 shells,但是是其他程序,要求它们支持与真实 shells 相同的选项可能会有问题)。
对于 运行 一个带有 execlp
的命令,我们执行
execlp("ps", "ps", NULL);
这里可以看出冗余,因为我们传递了两次 ps
。此行为在所有 exec
变体中都是一致的。
为什么 exec 需要这样的冗余?为什么不写成我们可以简单
execlp("ps", NULL);
第二个参数应该与第一个参数匹配,但不需要相同。
根据文档,
argv
is an array of argument strings passed to the new program. By convention, the first of these strings should contain the filename associated with the file being executed.
如果您有令人信服的理由对您计划 运行 的程序隐藏正在执行的文件的名称,您可以为第二个参数传递任何您想要的字符串,包括空的或一个 NULL
字符串:
execlp("ps", "<hidden>", NULL);
第一个参数是要执行的文件路径(/bin/ls
,/home/development/myproject/foo
)。其余参数对应于传递给 main
的 argv
向量。想象一下在 shell 中输入以下内容:
$ ./foo bar bletch
可执行路径是 ./foo
- 这是传递给 execlp
的第一个参数。按照惯例,argv[0]
应该是用于调用程序的字符串,因此完整的 argv
向量是 {"./foo", "bar", "bletch", NULL}
。因此,
execlp( "./foo", /* executable path */
"./foo", /* argv[0] */
"bar", /* argv[1] */
"bletch", /* argv[2] */
NULL /* argv[3] */
);
您可能不希望 argv[0]
与实际命令路径相同(比如因为路径是别名或者您不想公开确切的路径),在这种情况下您可以使用类似
execlp( "./secret/path/to/foo", "./foo", "bar", "bletch", NULL );
您不必为程序提供可执行文件的名称作为其 argv[0]
。你可以编写一个程序(rvw11.c
编译为 rvw11
可执行文件):
#include <unistd.h>
int main(void)
{
execlp("sleep", "rip van winkle", "40", (char *)0);
return 1;
}
你会发现如果你 运行:
$ ./rvw11 &
[1] 53034
$ ps
PID TTY TIME CMD
515 ttys000 0:00.07 -bash
534 ttys002 0:00.09 -bash
543 ttys003 0:01.15 -bash
558 ttys004 0:00.32 -bash
53034 ttys004 0:00.01 rip van winkle 40
$
因此,ps
列表中显示的名称是参数列表中作为 argv[0]
给出的值,而不是可执行文件的名称。 (在 macOS Sierra 10.12.2 上演示,但在大多数类 Unix 系统上你会得到类似的结果。)
这也说明了为什么从参数列表中确定可执行文件的名称不一定可行。
其他答案已经解释说您可以提供与程序名称不同的 argv[0]
,但没有解释 为什么 您可能想要这样做。
一些程序的行为因调用它们所用的名称而异。一个常见的例子是 shells,例如sh
、bash
和 csh
。他们检查 argv[0]
的第一个字符,如果这是 -
,他们将作为登录 shell 而不是常规的 shell。因此,当 /bin/login
调用用户登录 shell 时,它会执行以下操作:
execlp("/bin/bash", "-bash", (char*)NULL);
这样,bash
知道它是 运行 作为登录的一部分,并且可以相应地进行操作。这可以使用选项参数来完成,但是每个可能用作登录 shell 的程序都必须识别该选项(一些特殊的用户名有登录 shell,但实际上并不是 shells,但是是其他程序,要求它们支持与真实 shells 相同的选项可能会有问题)。