Является ли argv [0] = имя исполняемого файла принятым стандартом или просто общим соглашением? - PullRequest
90 голосов
/ 12 января 2010

При передаче аргумента main() в приложении на C или C ++, argv[0] всегда будет именем исполняемого файла? Или это просто общее соглашение, которое не гарантируется в 100% случаев?

Ответы [ 8 ]

103 голосов
/ 12 января 2010

Догадки (даже образованные догадки) - это весело, но вам действительно нужно обратиться к документам стандартов, чтобы быть уверенным.Например, ISO C11 заявляет (мой акцент):

Если значение argc больше нуля, строка, на которую указывает argv[0] , представляет название программы;argv[0][0] должен быть нулевым символом, если имя программы недоступно из среды хоста.

Так что нет, это только имя программы, если это имя доступно. Ион "представляет" имя программы, необязательно - имя программы.В предыдущем разделе говорится:

Если значение argc больше нуля, элементы массива с argv[0] по argv[argc-1] включительно должны содержать указатели на строки, которые задаются реализациейзначения в среде хоста до запуска программы.

Это не отличается от C99, предыдущего стандарта, и означает, что даже значения не определяются стандартом - он работаетполностью к реализации.

Это означает, что имя программы может быть пустым, если среда хоста не не предоставляет его, и что-нибудь еще, если среда хоста делает предоставьте это, при условии, что "что-нибудь еще" так или иначе представляет имя программы.В мои более садистские моменты я бы подумал о том, чтобы перевести его на суахили, запустить его через шифр подстановки и затем сохранить его в обратном порядке байтов: -).

Однако, определенный в реализации имеет конкретное значение в стандартах ISO - реализация должна документировать, как это работает.Таким образом, даже UNIX, который может помещать все, что ему нравится, в argv[0] с помощью семейства вызовов exec, должен (и делает) документировать это.

45 голосов
/ 12 января 2010

В системах типа *nix с вызовами exec*(), argv[0] будет тем, что звонящий ставит в argv0 место в вызове exec*().

Оболочка использует соглашение, что это имя программы, и большинство других программ придерживаются того же соглашения, поэтому argv[0] обычно имя программы.

Но мошенническая программа Unix может вызывать exec() и делать argv[0] все, что угодно, поэтому, независимо от того, что говорит стандарт C, вы не можете рассчитывать на это 100% времени.

8 голосов
/ 12 января 2010

Согласно стандарту C ++, раздел 3.6.1:

argv [0] должен быть указателем на начальный характер NTMBS, который представляет имя, используемое для вызова программа или ""

Так что нет, это не гарантируется, по крайней мере, стандартом.

4 голосов
/ 12 января 2010

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

И тогда вам просто нужно проанализировать строку, чтобы извлечь имя исполняемого файла из пути.

4 голосов
/ 12 января 2010

На этой странице говорится:

Элемент argv [0] обычно содержит название программы, но на это не следует полагаться - в любом случае, для программы необычно не знать свое имя!

Однако другие страницы, похоже, подтверждают тот факт, что это всегда имя исполняемого файла. Этот гласит:

Вы заметите, что argv [0] - это путь и имя самой программы. Это позволяет программе обнаруживать информацию о себе. Он также добавляет еще один в массив аргументов программы, поэтому распространенной ошибкой при извлечении аргументов командной строки является получение argv [0], когда вы хотите argv [1].

3 голосов

Приложения с argv[0] != исполняемым именем

  • многие оболочки определяют, являются ли они оболочкой входа в систему, проверяя argv[0][0] == '-'. Оболочки для входа имеют разные свойства, в частности, они исходят из некоторых файлов по умолчанию, таких как /etc/profile.

    Как правило, сам инициал или 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

Runnable POSIX execve пример, где argv[0] != имя исполняемого файла

Другие упомянутые exec, но вот работоспособный пример.

a.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *argv[] = {"yada yada", NULL};
    char *envp[] = {NULL};
    execve("b.out", argv, envp);
}

b.c

#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 голосов
/ 12 января 2010

Я не уверен, является ли это почти универсальным соглашением или стандартом, но в любом случае вы должны его соблюдать. Я никогда не видел, чтобы его использовали вне Unix и Unix-подобных систем. В средах Unix - и, возможно, особенно в старые времена - программы могут иметь существенно различное поведение в зависимости от имени, под которым они вызываются.

РЕДАКТИРОВАНИЕ: Я вижу из других постов одновременно с моим, что кто-то идентифицировал его как принадлежащий определенному стандарту, но я уверен, что соглашение давно предшествует стандарту.

0 голосов
/ 23 ноября 2018

Если вы запустите программу Amiga с помощью Workbench, argv [0] не будет установлен, только с помощью CLI.

...