Чи є “argv [0] = назва виконуваного файлу” прийнятим стандартом чи просто загальноприйнятою умовою?


102

При передачі аргументу main()в програму C або C ++, argv[0]завжди буде ім’я виконуваного файлу? Або це просто загальноприйнята конвенція і не гарантується, що це буде правдою у 100% випадків?


20
В Unix, розглянемо: execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. Ім'я виконуваного файлу не має відношення до значення в argv[0].
Джонатан Леффлер,

Відповіді:


119

Відгадки (навіть освічені здогадки) - це цікаво, але вам справді потрібно перейти до стандартних документів, щоб бути впевненим. Наприклад, ISO C11 говорить (наголошую):

Якщо значення argcбільше нуля, рядок, на який вказує символ, argv[0] представляє назву програми; argv[0][0]має бути нульовим символом, якщо назва програми недоступна із середовища хосту.

Так що ні, це лише назва програми, якщо це ім’я доступне. І воно "представляє" назву програми, не обов'язково це назва програми. У попередньому розділі зазначено:

Якщо значення argcбільше нуля, члени масиву argv[0]через argv[argc-1]включно повинні містити вказівники на рядки, яким середовище хоста надає визначені реалізацією значення перед запуском програми.

Це не змінилося порівняно з попереднім стандартом C99 і означає, що навіть значення не диктуються стандартом - це залежить від реалізації повністю.

Це означає, що назва програми може бути порожньою, якщо середовище хоста її не надає, і будь-що інше, якщо середовище хоста надає надає, за умови, що "будь-що інше" якось представляє назву програми. У мої більш садистичні моменти я б міг перекласти його на суахілі, пропустивши через замінний шифр, а потім зберігаючи в зворотному порядку байтів :-).

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


3
Це може бути стандартом, але unix просто не застосовує його, і ви не можете на нього розраховувати.
dmckee --- екс-модератор кошеня

4
Питання не кажучи вже про UNIX взагалі . Це було запитання C простим і простим, отже ISO C є довідковим документом. Назва програми - це реалізація, визначена в стандарті, тому реалізація може вільно робити те, що вона хоче, включаючи дозволяючи щось там, що не є справжньою назвою - я думав, я це зрозумів у передостанньому реченні.
paxdiablo

2
Пакс, я не проголосував проти тебе і не схвалюю тих, хто дав відповідь, бо ця відповідь настільки авторитетна, наскільки може отримати. Але я думаю, що ненадійність вартості argv[0]належить до програмування в реальному світі.
dmckee --- кошеня екс-модератора

4
@caf, це правильно. Я бачив, що він містить такі різноманітні речі, як повний шлях програми ('/ progpath / prog'), лише ім'я файлу ('prog'), трохи змінене ім'я ('-prog'), описове ім'я (' prog - програма для прогадування ') і нічого (' '). Реалізація повинна визначити, що вона містить, але це все, що вимагає стандарт.
paxdiablo

3
Дякую всім! Велика дискусія з (здавалося б) простого питання. Хоча відповідь Річарда справедлива для операційних систем * nix, я вибрав відповідь paxdiablo, тому що мене менше цікавить поведінка конкретної ОС, і в першу чергу цікавиться існуванням (або відсутністю) прийнятого стандарту. (Якщо вам цікаво: У контексті вихідного запитання - у мене немає операційної системи. Я пишу код для побудови необробленого буфера argc / argv для виконуваного файлу, завантаженого на вбудований пристрій, і мені потрібно було знати, що мені робити з argv [0]). +1 StackOverflow за те, що ви чудові!
Mike Willekes

49

Під *nixтиповими системами із exec*()дзвінками argv[0]буде те, що абонент поміщає на argv0місце вexec*() дзвінку.

Оболонка використовує домовленість, що це назва програми, і більшість інших програм дотримуються тієї ж угоди, тому 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 , відповідає умовам, поки вона вказує, що це ім'я. Сюди входить можливість відверто підробляти назву програми, завантажуючи argv [0] чим завгодно у сімействі викликів exec.
paxdiablo

Ось і краса слова "представляє" у стандарті, коли воно посилається на argv [0] ("воно представляє назву програми") та argv [1..N] ("вони представляють аргументи програми"). "незавантажена ластівка" - діюча назва програми.
Річард Пеннінгтон,

9

Відповідно до стандарту C ++, розділ 3.6.1:

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

Так що ні, це не гарантується, принаймні стандартом.


5
Я припускаю, що це багатобайтний рядок із нульовим закінченням?
paxdiablo

6

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слід використовувати, щоб отримати будь-який шлях, але лише наявність коду є хорошим керівництвом).
ура та хт. - Альф

4

Додатки, що мають argv[0] !=виконуване ім'я

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

    Зазвичай це сам init або gettyдодає провідне -, див. Також: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -вбудувати / 300152 # 300152

  • двійкові файли для багатовикликових дзвінків, можливо, найбільш помітно Busybox . Ці символьні посилання містять кілька імен, наприклад, /bin/shі /bin/lsдо одного виконуваного /bin/busybox, який розпізнає, з якого інструменту використовуватиargv[0] .

    Це дає можливість мати один невеликий статично зв’язаний виконуваний файл, який представляє безліч інструментів, і який буде працювати в основному в будь-якому середовищі Linux.

Дивіться також: /unix/315812/why-does-argv-include-the-program-name/315817

execveПриклад запуску POSIX, де 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.


3

На цій сторінці вказано:

Елемент argv [0] зазвичай містить ім'я програми, але на це покладатися не слід - у будь-якому випадку для програми незвично не знати власного імені!

Однак інші сторінки, схоже, підтверджують той факт, що це завжди ім'я виконуваного файлу. Цей стверджує:

Ви помітите, що argv [0] - це шлях та назва самої програми. Це дозволяє програмі виявляти інформацію про себе. Він також додає ще один до масиву аргументів програми, тому типовою помилкою при отриманні аргументів командного рядка є захоплення argv [0], коли ви хочете argv [1].


11
Деякі програми користуються тим, що не знають імені, за допомогою якого їх викликали. Я вважаю, що BusyBox ( busybox.net/about.html ) працює таким чином. Існує лише один виконуваний файл, який реалізує безліч різних утиліт командного рядка. Він використовує купу символічних посилань та argv [0], щоб визначити, який інструмент командного рядка слід запускати
Трент,

Так, я пам’ятаю, як я помітив, що "gunzip" є символічним посиланням на "gzip", і на мить здивувався, як це працює.
Девід Торнлі,

2
Багато програм шукають інформацію на argv [0]; наприклад, якщо останній компонент імені починається з тире (наприклад, '/ bin / -sh'), тоді оболонка буде запускати профіль та інші речі, як для оболонки для входу.
Джонатан Леффлер

2
@Jon: Я думав, що оболонки для входу були розпочаті argv[0]="-/bin/sh"? Так і є на всіх машинах, якими я користувався.
ефемієнт

3

Я не впевнений, що це майже універсальна конвенція чи стандарт, але в будь-якому випадку ви повинні дотримуватися цього. Однак я ніколи не бачив, щоб він експлуатувався поза Unix та Unix-подібними системами. У середовищах Unix - і, можливо, особливо за старих часів - програми можуть мати суттєво різну поведінку залежно від імені, під яким вони викликаються.

ВИДАВАНО: Я бачу з інших постів одночасно з моїм, що хтось визначив це як вихід із певного стандарту, але я впевнений, що конвенція давно передує стандарту.


1
Я справді бажаю, щоб, якщо люди збираються "позначити" мою відповідь, вони могли б вказати, що їм у цьому не подобається.
Joe Mabel

0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.