Bei der Übergabe von Argumenten an main()
in einer C- oder C++-Anwendung, wird argv[0]
immer der Name der ausführbaren Datei sein? Oder ist dies nur eine gängige Konvention und nicht zu 100 % garantiert?
Antworten
Zu viele Anzeigen?Vermutungen (sogar fundierte Vermutungen) machen Spaß, aber um sicher zu gehen, müssen Sie sich die Normungsdokumente ansehen. In ISO C11 heißt es zum Beispiel (meine Hervorhebung):
Wenn der Wert von
argc
größer als Null ist, wird die Zeichenkette, auf die mitargv[0]
steht für den Programmnamen;argv[0][0]
ist das Nullzeichen, wenn der Programmname in der Hostumgebung nicht verfügbar ist.
Also nein, es ist nur der Programmname, wenn dieser Name verfügbar. Und es "repräsentiert" der Programmname, nicht unbedingt est den Programmnamen. Im Abschnitt davor steht:
Wenn der Wert von
argc
größer als Null ist, werden die Arrayelementeargv[0]
überargv[argc-1]
inclusive muss Zeiger auf Zeichenketten enthalten, die von der Host-Umgebung vor dem Programmstart mit implementierungsdefinierten Werten versehen werden.
Dies ist unverändert gegenüber C99, dem vorherigen Standard, und bedeutet, dass auch die Werte sind nicht durch die Norm vorgegeben - es liegt ganz an der Implementierung.
Das bedeutet, dass der Programmname leer sein kann, wenn die Hostumgebung nicht zur Verfügung stellen, und alles andere, wenn die Host-Umgebung hace zur Verfügung stellen, vorausgesetzt, dass "irgendetwas anderes" irgendwie den Programmnamen darstellt. In meinen sadistischeren Momenten würde ich in Erwägung ziehen, ihn ins Suaheli zu übersetzen, ihn durch eine Substitutions-Chiffre laufen zu lassen und ihn dann in umgekehrter Byte-Reihenfolge zu speichern :-).
Die Umsetzung wird jedoch durch hace haben in den ISO-Normen eine bestimmte Bedeutung - die Implementierung muss dokumentieren, wie sie funktioniert. So kann sogar UNIX, das alles Mögliche in argv[0]
mit dem exec
Familie von Aufrufen, muss dies dokumentieren (und tut es auch).
Unter *nix
Systeme des Typs mit exec*()
Anrufe, argv[0]
wird das sein, was der Aufrufer in die argv0
Stelle im exec*()
anrufen.
Die Shell verwendet die Konvention, dass dies der Programmname ist, und die meisten anderen Programme folgen der gleichen Konvention, also argv[0]
normalerweise der Programmname.
Aber ein bösartiges Unix-Programm kann die exec()
und machen argv[0]
Egal, was der C-Standard sagt, man kann sich nicht zu 100 % darauf verlassen.
ISO-IEC 9899 besagt:
5.1.2.2.1 Start des Programms
Wenn der Wert von
argc
größer als Null ist, wird die Zeichenkette, auf die mitargv[0]
steht für den Programmnamen;argv[0][0]
ist das Nullzeichen, wenn der Programmname in der Host-Umgebung nicht verfügbar ist. Wenn der Wert vonargc
größer als eins ist, werden die Zeichenketten, auf die mitargv[1]
überargv[argc-1]
stellen die Programmparameter .
Ich habe auch verwendet:
#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) */
Und dann müssen Sie nur noch die Zeichenkette analysieren, um den Namen der ausführbaren Datei aus dem Pfad zu extrahieren.
Anwendungen von Haben argv[0] !=
Name der ausführbaren Datei
-
Viele Shells stellen fest, ob sie eine Login-Shell sind, indem sie prüfen
argv[0][0] == '-'
. Login-Shells haben unterschiedliche Eigenschaften, vor allem, dass sie einige Standarddateien wie/etc/profile
.In der Regel ist es das Init selbst oder
getty
die den führenden-
, siehe auch: https://unix.stackexchange.com/questions/299408/how-to-login-automatically-without-typing-the-Root-username-or-password-in-build/300152#300152 -
Binärdateien mit mehreren Aufrufen, vielleicht am bemerkenswertesten Busybox . Diese Symlinks verbinden mehrere Namen, z. B.
/bin/sh
y/bin/ls
zu einer einzigen auswertbaren/bin/busybox
die erkennt, welches Werkzeug zu verwenden ist ausargv[0]
.Dadurch ist es möglich, eine einzige kleine, statisch gelinkte ausführbare Datei zu haben, die mehrere Tools repräsentiert, und die im Grunde auf jeder Linux-Umgebung funktioniert.
Siehe auch: https://unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817
Lauffähiges POSIX execve
Beispiel, bei dem argv[0] !=
Name der ausführbaren Datei
Andere erwähnten exec
aber hier ist ein lauffähiges Beispiel.
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]);
}
Dann:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
Gibt:
yada yada
Ja, argv[0]
könnte auch sein:
Getestet auf Ubuntu 16.10.
- See previous answers
- Weitere Antworten anzeigen