При передаче аргумента main()
в приложении C или C++ будет argv[0]
всегда быть именем исполняемого файла? Или это просто обычное соглашение и не гарантируется в 100% случаев?Является ли «argv [0] = имя исполняемого» принятым стандартом или просто общим соглашением?
ответ
Удовлетворение (даже образованные догадки) - это весело, но вам действительно нужно пойти в документы стандартов, чтобы быть уверенным. Например, ISO C11 состояния (курсив мой):
Если значение
argc
больше нуля, то строка, на которую указываетargv[0]
представляет название программы;argv[0][0]
должен быть нулевым символом, если имя программы недоступно в среде хоста.
Нет, это только имя программы, если это имя . И это «представляет» название программы, не обязательно является название программы. Раздел до того, что сказано:
Если значение
argc
больше нуля, члены массиваargv[0]
черезargv[argc-1]
включительно должны содержать указатели на строки, которые приведены реализации определенных значений принимающей окружающей среды до запуска программы ,
Это не отличается от C99, предыдущего стандарта, и означает, что даже значения не диктуются стандартом - это до реализации полностью.
Это означает, что имя программы может быть пустым, если принимающая среда не обеспечить его, и все остальное, если принимающая среда делает обеспечить его, при условии, что «все остальное» каким-то образом представляет собой название программы. В мои более садистские моменты я бы подумал о переводе его на суахили, запустив его через шифр замещения, а затем сохранил его в обратном порядке байта :-).
Однако в определении реализации есть определенное значение в стандартах ИСО - реализация должна документировать, как это работает. Таким образом, даже UNIX, который может поместить все, что ему нравится, в argv[0]
с семейством вызовов exec
, должен (и делает) документировать его.
Можете ли вы добавить ссылку на этот документ стандартов, пожалуйста, чтобы мы все могли прочитать? Благодарю. – liwp
Это может быть стандарт, но unix просто не применяет его, и вы не можете рассчитывать на него. – dmckee
Вопрос не упоминал UNIX * вообще *. Вопрос был простым и простым, поэтому ISO C является справочным документом. Название программы - это реализация, определенная в стандарте, поэтому реализация может свободно делать то, что она хочет, в том числе разрешать что-то там, где это не настоящее имя, - я думал, что сделал это ясно в предпоследнем предложении. – paxdiablo
This page состояния:
элемент ARGV [0] обычно содержит название программы, но это не следует полагаться - в любом случае это необычно для программы не знать свое собственное имя!
Однако, другие страницы, похоже, поддерживают тот факт, что это всегда имя исполняемого файла. This one указывается:
Вы заметите, что argv [0] - это путь и имя самой программы. Это позволяет программе обнаруживать информацию о себе. Он также добавляет еще один массив аргументов программы, поэтому общая ошибка при извлечении аргументов из командной строки заключается в том, чтобы захватить argv [0], когда вы хотите argv [1].
Некоторые программы используют тот факт, что они не знают имя, которое использовалось для их вызова. Я считаю, что BusyBox (http://www.busybox.net/about.html) работает таким образом. Существует только один исполняемый файл, реализующий множество различных утилит командной строки. Он использует кучу символических ссылок и argv [0], чтобы определить, какой инструмент командной строки должен быть запущен. – Trent
Да, я помню, заметив, что «gunzip» был символической ссылкой на «gzip» и некоторое время размышлял над тем, как это работает. –
Многие программы смотрят на argv [0] для информации; например, если последний компонент имени начинается с тире (например,/bin/-sh), то оболочка будет запускать профиль и другие материалы, как для оболочки входа. –
Согласно стандарту C++, раздел 3.6.1:
ARGV [0] должен быть указатель на исходного характера NTMBS, что представляет собой имя, используемое для вызова программы или ""
Нет, это не гарантируется, по крайней мере, стандартом.
Я предполагаю, что это пустая строка с длинными байтами? – paxdiablo
Да, действительно. – 2010-01-12 17:47:31
Под *nix
систем типа с exec*()
вызовов, argv[0]
будет то, что абонент помещает в argv0
место в exec*()
вызова.
В оболочке используется соглашение о том, что это имя программы, и большинство других программ соответствуют одному и тому же соглашению, поэтому argv[0]
обычно является именем программы.
Но программа-мошенник Unix может позвонить exec()
и сделать argv[0]
всем, что ему нравится, поэтому независимо от того, что говорит стандарт C, вы не можете рассчитывать на это 100% времени.
Это лучший ответ, чем выше paxdiablo. Стандарт просто называет это «имя программы», но это не применяется нигде, насколько мне известно. Ядра Unix равномерно передают строку, переданную execve(), без изменений в дочерний процесс. –
Стандарт C ограничен в том, что он может сказать, потому что он не знает о «execve()» и т. Д. Стандарт POSIX (http://www.opengroup.org/onlinepubs/9699919799/functions/execve.html) имеет более того - ясно, что то, что находится в argv [0], по прихоти процесса выполняет «execve()» (или связанный) системный вызов. –
@ Энди, ты можешь свободно высказывать свое мнение :-) Но ты не прав насчет принудительного исполнения. Если реализация не соответствует стандарту, то она не соответствует требованиям. И на самом деле, поскольку это реализация, определяющая, что такое «имя программы», такая ОС, как UNIX *, соответствует *, если она задает имя. Это включает в себя возможность явно подделывать имя программы, загружая argv [0] с помощью чего угодно в семействе вызовов exec. – paxdiablo
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 и выше. – ephemient
Упрощенный для кода (не сказать, что это идеальный или правильный, например, в Windows 'GetModuleFileNameW' должен использоваться, чтобы иметь возможность получить любой путь, но наличие кода является хорошим руководством). –
BTW, в последнее время я выпустил https://github.com/gpakosz/whereami –
Я не уверен, является ли это почти универсальным соглашением или стандартом, но в любом случае вы должны соблюдать его. Однако я никогда не видел, чтобы он использовался вне Unix и Unix-подобных систем. В средах Unix - и, возможно, особенно в старые времена - программы могут иметь существенно различное поведение в зависимости от имени, под которым они вызывается.
EDITED: Я вижу из других сообщений одновременно с моим, что кто-то определил его как исходящий из определенного стандарта, но я уверен, что соглашение давно предшествует стандарту.
Я уверен, что если люди собираются «пометить» мой ответ, они дадут некоторое представление о том, что им не нравится в этом. –
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.
В Unix рассмотрим: 'execl ("/home/hacker/.hidden/malicious ","/bin/ls "," -s ", (char *) 0);'. Имя исполняемого файла не имеет отношения к значению в 'argv [0]'. –