“ argv [0] =可执行名称”是公认的标准还是仅仅是一个通用约定?


102

main()在C或C ++应用程序中将参数传递给时,将argv[0]始终是可执行文件的名称吗?还是这只是一个惯例,不能保证100%的时间都是正确的?


19
在Unix上,请考虑:execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);。可执行文件的名称与中的值无关argv[0]
乔纳森·莱夫勒

Answers:


118

猜测(甚至是受过教育的猜测)很有趣,但是您确实需要查阅标准文档才能确定。例如,ISO C11声明(我强调):

如果的值argc大于零,则由指向的字符串argv[0] 表示程序名称;argv[0][0]如果程序名称在主机环境中不可用,则为空字符。

因此,不可以,只有程序名称可用。并且它“代表”程序名称,不一定程序名称。之前的部分指出:

如果值argc是大于零,阵列成员argv[0]通过argv[argc-1]包容应包含指向字符串,其通过前程序启动主机环境给定实现所定义的值。

这与以前的标准C99保持不变,这意味着即使也不是由该标准规定的-这完全取决于实现方式。

这意味着,节目名称可以是空的,如果主机环境如果主机环境提供它,别的提供的,规定“任何东西”在某种程度上代表了程序名称。在更悲伤的时刻,我会考虑将其转换为斯瓦希里语,通过替换密码运行它,然后以相反的字节顺序存储它:-)。

但是,实现定义在ISO标准中确实具有特定含义-实现必须记录其工作方式。因此,即使UNIX,它可以把任何东西它喜欢到argv[0]exec家人通话的,有(并执行)文件就可以了。


3
那可能是标准,但是unix根本不执行它,因此您不能指望它。
dmckee ---前主持人小猫,2010年

4
这个问题没有提到UNIX 可言。这是一个简单而简单的C问题,因此ISO C是参考文档。程序名称是标准中定义的实现,因此实现可以自由执行所需的操作,包括在其中允许使用非实际名称的内容-我想我已经在倒数第二句中明确了这一点。
paxdiablo 2010年

2
大同,我没有投票您失望的,不批准这些谁没有,因为这个答案是权威的,因为它的得。但是我确实认为,值的不可靠性argv[0]与现实世界中的编程有关。
dmckee ---前主持人小猫,2010年

4
@caf,没错。我已经看到它拥有各种不同的功能,例如程序的完整路径('/ progpath / prog'),文件名('prog'),稍微修改的名称('-prog'),描述性名称(' prog-用于进行')和什么都不做('')的程序。该实现必须定义其内容,但这是标准所需要的。
paxdiablo

3
感谢大家!一个(看似)简单问题的精彩讨论。尽管Richard的答案对* nix操作系统有效,但我选择paxdiablo的答案是因为我对特定OS的行为不太感兴趣,而主要是对是否存在公认的标准感兴趣。(如果您很好奇:在原始问题的上下文中-我没有操作系统。我正在编写代码来构建原始argc / argv缓冲区,用于将可执行文件加载到嵌入式设备上,并且需要知道我该怎么做与argv [0])。为StackOverflow +1了不起!
Mike Willekes 2010年

48

*nix带有exec*()呼叫的类型系统下,argv[0]呼叫者将放置argv0exec*()呼叫中的任何位置。

Shell使用惯例,即这是程序名称,而大多数其他程序遵循相同的惯例,因此argv[0]通常是程序名称。

但是流氓的Unix程序可以调用exec()并进行argv[0]任何喜欢的操作,因此,无论C标准怎么说,您都无法100%地依靠它。


4
比上面的paxdiablo更好的答案。该标准仅将其称为“程序名称”,但是据我所知,并没有在任何地方强制执行。Unix内核统一将传递给execve()的字符串不变地传递给子进程。
安迪·罗斯

4
C标准只能说些什么,因为它不了解'execve()'等。POSIX标准(opengroup.org/onlinepubs/9699919799/functions/execve.html)还有更多话要说-明确说明argv [0]中的内容是在处理过程中产生的,因此会执行“ execve()”(或相关的)系统调用。
乔纳森·莱夫勒

1
@Andy,您可以自由发表您的意见:-)但是您在执行方面错了。如果实施不符合标准,则不符合标准。而事实上,因为它的实现定义,以什么“节目名称”,如UNIX操作系统,只要它指定名字是什么符合。这包括能够通过在exec调用系列中加载您想要的任何内容来公然伪造程序名称。
paxdiablo 2010年

当它引用argv [0](“它代表程序名称”)和argv [1..N](“它们代表程序参数”)时,这就是标准中“代表”一词的美。“空载吞咽”是有效的程序名称。
理查德·彭宁顿

8

根据C ++标准第3.6.1节:

argv [0]是指向NTMBS初始字符的指针,该字符代表用于调用程序的名称或“”

因此,不,至少不能由标准来保证。


5
我认为这是以null结尾的多字节字符串吗?
paxdiablo

5

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) */

然后,您只需解析字符串即可从路径中提取可执行文件名称。


2
/proc/self/path/a.out符号链接可能在Solaris 10及更高版本上可用。
短暂

推荐使用该代码(并不是说它是理想的或正确的,例如,在Windows上GetModuleFileNameW应该使用它来检索任何路径,而仅存在代码才是很好的指导)。
干杯和健康。-Alf

4

该页面指出:

元素argv [0]通常包含程序的名称,但是不应依赖该名称-无论如何,程序不知道自己的名称是不寻常的!

但是,其他页面似乎支持以下事实:它始终是可执行文件的名称。这个陈述:

您会注意到argv [0]是程序本身的路径和名称。这使程序可以发现有关其自身的信息。它还向程序参数数组中添加了一个,因此在获取命令行参数时的常见错误是在需要argv [1]时获取argv [0]。


11
一些程序利用了一个事实,即它们不知道用于调用它们的名称。我相信BusyBox(busybox.net/about.html)可以这样工作。只有一个可执行文件,可实现许多不同的命令行实用程序。它使用一堆符号链接和argv [0]确定应运行哪种命令行工具
Trent,2010年

是的,我记得曾经注意到“ gunzip”是“ gzip”的符号链接,并且想知道它是如何工作的。
David Thornley,2010年

2
许多程序会在argv [0]中寻求信息。例如,如果名称的最后一个部分以破折号(例如'/ bin / -sh')开头,则该外壳程序将运行配置文件和其他内容,就像登录外壳程序一样。
乔纳森·莱夫勒

2
@Jon:我以为登录Shell是从开始的argv[0]="-/bin/sh"?无论如何,在我使用过的所有机器上都是如此。
短暂

3

具有argv[0] !=可执行名称的应用

另请参阅: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上测试。


2

我不确定这是近乎通用的惯例还是标准,但是无论哪种方式,您都应该遵守。但是,我从未见过它在Unix和类似Unix的系统之外被利用。在Unix环境中-尤其是在过去-程序可能会根据调用它们的名称而具有明显不同的行为。

编辑:我与其他人同时看到其他人已经将其标识为来自特定标准,但是我确信公约早于该标准。


1
我当然希望,如果人们要“记下”我的回答,他们会给出一些表示不满意的地方。
Joe Mabel '01

0

如果通过Workbench argv [0]启动Amiga程序,则仅通过CLI不会设置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.