При передачі аргументу main()
в програму C або C ++, argv[0]
завжди буде ім’я виконуваного файлу? Або це просто загальноприйнята конвенція і не гарантується, що це буде правдою у 100% випадків?
При передачі аргументу main()
в програму C або C ++, argv[0]
завжди буде ім’я виконуваного файлу? Або це просто загальноприйнята конвенція і не гарантується, що це буде правдою у 100% випадків?
Відповіді:
Відгадки (навіть освічені здогадки) - це цікаво, але вам справді потрібно перейти до стандартних документів, щоб бути впевненим. Наприклад, 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*()
дзвінку.
Оболонка використовує домовленість, що це назва програми, і більшість інших програм дотримуються тієї ж угоди, тому 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][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.
На цій сторінці вказано:
Елемент argv [0] зазвичай містить ім'я програми, але на це покладатися не слід - у будь-якому випадку для програми незвично не знати власного імені!
Однак інші сторінки, схоже, підтверджують той факт, що це завжди ім'я виконуваного файлу. Цей стверджує:
Ви помітите, що argv [0] - це шлях та назва самої програми. Це дозволяє програмі виявляти інформацію про себе. Він також додає ще один до масиву аргументів програми, тому типовою помилкою при отриманні аргументів командного рядка є захоплення argv [0], коли ви хочете argv [1].
argv[0]="-/bin/sh"
? Так і є на всіх машинах, якими я користувався.
Я не впевнений, що це майже універсальна конвенція чи стандарт, але в будь-якому випадку ви повинні дотримуватися цього. Однак я ніколи не бачив, щоб він експлуатувався поза Unix та Unix-подібними системами. У середовищах Unix - і, можливо, особливо за старих часів - програми можуть мати суттєво різну поведінку залежно від імені, під яким вони викликаються.
ВИДАВАНО: Я бачу з інших постів одночасно з моїм, що хтось визначив це як вихід із певного стандарту, але я впевнений, що конвенція давно передує стандарту.
Якщо ви запустите програму Amiga за допомогою Workbench, argv [0] буде встановлено не лише CLI.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Ім'я виконуваного файлу не має відношення до значення вargv[0]
.