Answers:
猜测(甚至是受过教育的猜测)很有趣,但是您确实需要查阅标准文档才能确定。例如,ISO C11声明(我强调):
如果的值
argc
大于零,则由指向的字符串argv[0]
表示程序名称;argv[0][0]
如果程序名称在主机环境中不可用,则为空字符。
因此,不可以,只有程序名称可用。并且它“代表”程序名称,不一定是程序名称。之前的部分指出:
如果值
argc
是大于零,阵列成员argv[0]
通过argv[argc-1]
包容应包含指向字符串,其通过前程序启动主机环境给定实现所定义的值。
这与以前的标准C99保持不变,这意味着即使值也不是由该标准规定的-这完全取决于实现方式。
这意味着,节目名称可以是空的,如果主机环境不如果主机环境提供它,别的不提供的,规定“任何东西”在某种程度上代表了程序名称。在更悲伤的时刻,我会考虑将其转换为斯瓦希里语,通过替换密码运行它,然后以相反的字节顺序存储它:-)。
但是,实现定义在ISO标准中确实具有特定含义-实现必须记录其工作方式。因此,即使UNIX,它可以把任何东西它喜欢到argv[0]
与exec
家人通话的,有(并执行)文件就可以了。
argv[0]
与现实世界中的编程有关。
在*nix
带有exec*()
呼叫的类型系统下,argv[0]
呼叫者将放置argv0
在exec*()
呼叫中的任何位置。
Shell使用惯例,即这是程序名称,而大多数其他程序遵循相同的惯例,因此argv[0]
通常是程序名称。
但是流氓的Unix程序可以调用exec()
并进行argv[0]
任何喜欢的操作,因此,无论C标准怎么说,您都无法100%地依靠它。
根据C ++标准第3.6.1节:
argv [0]是指向NTMBS初始字符的指针,该字符代表用于调用程序的名称或“”
因此,不,至少不能由标准来保证。
ISO-IEC 9899规定:
5.1.2.2.1程序启动
如果的值
argc
大于零,则由指向的字符串argv[0]
表示程序名;否则,表示程序名。argv[0][0]
如果程序名称在主机环境中不可用,则为空字符。如果值argc
大于一,字符串由指向argv[1]
通过argv[argc-1]
表示程序参数。
我也用过:
#if defined(_WIN32)
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
}
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */
#include <unistd.h>
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
pathName[pathNameSize] = '\0';
return pathNameSize;
}
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
#include <mach-o/dyld.h>
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
uint32_t pathNameSize = 0;
_NSGetExecutablePath(NULL, &pathNameSize);
if (pathNameSize > pathNameCapacity)
pathNameSize = pathNameCapacity;
if (!_NSGetExecutablePath(pathName, &pathNameSize))
{
char real[PATH_MAX];
if (realpath(pathName, real) != NULL)
{
pathNameSize = strlen(real);
strncpy(pathName, real, pathNameSize);
}
return pathNameSize;
}
return 0;
}
#else /* else of: #elif defined(__APPLE__) */
#error provide your own implementation
#endif /* end of: #if defined(_WIN32) */
然后,您只需解析字符串即可从路径中提取可执行文件名称。
/proc/self/path/a.out
符号链接可能在Solaris 10及更高版本上可用。
GetModuleFileNameW
应该使用它来检索任何路径,而仅存在代码才是很好的指导)。
该页面指出:
元素argv [0]通常包含程序的名称,但是不应依赖该名称-无论如何,程序不知道自己的名称是不寻常的!
但是,其他页面似乎支持以下事实:它始终是可执行文件的名称。这个陈述:
您会注意到argv [0]是程序本身的路径和名称。这使程序可以发现有关其自身的信息。它还向程序参数数组中添加了一个,因此在获取命令行参数时的常见错误是在需要argv [1]时获取argv [0]。
argv[0]="-/bin/sh"
?无论如何,在我使用过的所有机器上都是如此。
具有argv[0] !=
可执行名称的应用
许多shell通过检查确定它们是否是登录shell argv[0][0] == '-'
。登录外壳具有不同的属性,特别是它们会提供一些默认文件,例如/etc/profile
。
它通常是init本身或getty
添加前导字符-
,另请参见:https : //unix.stackexchange.com/questions/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152#300152
多调用二进制文件,也许最著名的是Busybox。这些符号将多个名称链接在一起,例如/bin/sh
和链接/bin/ls
到一个可扩展的可执行文件/bin/busybox
,可以识别使用哪个工具argv[0]
。
这样就可以有一个小的静态链接的可执行文件,它代表多个工具,并且基本上可以在任何Linux环境上工作。
另请参阅:https : //unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817
可运行的POSIX execve
示例,其中的argv[0] !=
可执行文件名称
提到了其他人exec
,但这是一个可运行的示例。
交流电
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
公元前
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
然后:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
给出:
yada yada
是的,argv[0]
也可能是:
在Ubuntu 16.10上测试。
我不确定这是近乎通用的惯例还是标准,但是无论哪种方式,您都应该遵守。但是,我从未见过它在Unix和类似Unix的系统之外被利用。在Unix环境中-尤其是在过去-程序可能会根据调用它们的名称而具有明显不同的行为。
编辑:我与其他人同时看到其他人已经将其标识为来自特定标准,但是我确信公约早于该标准。
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
。可执行文件的名称与中的值无关argv[0]
。