Wird beim Übergeben von Argumenten an main()
eine C- oder C ++ - Anwendung argv[0]
immer der Name der ausführbaren Datei angegeben? Oder ist dies nur eine übliche Konvention und nicht garantiert, dass sie 100% der Zeit wahr ist?
Wird beim Übergeben von Argumenten an main()
eine C- oder C ++ - Anwendung argv[0]
immer der Name der ausführbaren Datei angegeben? Oder ist dies nur eine übliche Konvention und nicht garantiert, dass sie 100% der Zeit wahr ist?
Antworten:
Vermutungen (sogar fundierte Vermutungen) machen Spaß, aber Sie müssen unbedingt zu den Standarddokumenten gehen, um sicherzugehen. Zum Beispiel heißt es in ISO C11 (mein Schwerpunkt):
Wenn der Wert von
argc
größer als Null ist,argv[0]
repräsentiert die Zeichenfolge, auf die durch zeigt , den Programmnamen.argv[0][0]
muss das Nullzeichen sein, wenn der Programmname in der Hostumgebung nicht verfügbar ist.
Also nein, es ist nur der Programmname, wenn dieser Name verfügbar ist. Und es „steht für “ die Programmnamen, die nicht unbedingt ist der Programmname. Der vorhergehende Abschnitt besagt:
Wenn der Wert von
argc
größer als Null ist, müssen die Array-Mitgliederargv[0]
bisargv[argc-1]
einschließlich Zeiger auf Zeichenfolgen enthalten, denen vor dem Programmstart von der Host-Umgebung implementierungsdefinierte Werte zugewiesen werden.
Dies ist unverändert gegenüber C99, dem vorherigen Standard, und bedeutet, dass selbst die Werte nicht vom Standard vorgegeben werden - es liegt ganz bei der Implementierung.
Dies bedeutet, dass der Programmname leer sein kann, wenn die Hostumgebung ihn nicht bereitstellt, und alles andere, wenn die Hostumgebung ihn bereitstellt, vorausgesetzt, dass "alles andere" irgendwie den Programmnamen darstellt. In meinen sadistischeren Momenten würde ich in Betracht ziehen, es ins Suaheli zu übersetzen, es durch eine Substitutions-Chiffre zu führen und es dann in umgekehrter Bytereihenfolge zu speichern :-).
Implementierungsdefiniert hat jedoch eine spezifische Bedeutung in den ISO-Standards - die Implementierung muss dokumentieren, wie sie funktioniert. Sogar UNIX, das argv[0]
mit der exec
Familie der Aufrufe alles aufnehmen kann, was es will , muss (und tut) dies dokumentieren.
argv[0]
ist für die Programmierung in der realen Welt von Vorteil.
Unter *nix
Typsysteme mit exec*()
Anrufen argv[0]
wird alles angezeigt, was der Anrufer an die argv0
Stelle im exec*()
Anruf setzt.
Die Shell verwendet die Konvention, dass dies der Programmname ist, und die meisten anderen Programme folgen derselben Konvention, daher argv[0]
normalerweise der Programmname.
Aber ein betrügerisches Unix-Programm kann alles aufrufen exec()
und erstellen, argv[0]
was es möchte. Unabhängig davon, was der C-Standard sagt, können Sie sich nicht zu 100% darauf verlassen.
Gemäß dem C ++ Standard, Abschnitt 3.6.1:
argv [0] ist der Zeiger auf das Anfangszeichen eines NTMBS, der den Namen darstellt, der zum Aufrufen des Programms verwendet wird, oder ""
Also nein, es ist zumindest durch den Standard nicht garantiert.
In ISO-IEC 9899 heißt es:
5.1.2.2.1 Programmstart
Wenn der Wert von
argc
größer als Null ist,argv[0]
repräsentiert die Zeichenfolge, auf die durch zeigt , den Programmnamen.argv[0][0]
muss das Nullzeichen sein, wenn der Programmname in der Hostumgebung nicht verfügbar ist. Wenn der Wert vonargc
größer als eins ist, repräsentieren die Zeichenfolgen, auf die durch gezeigtargv[1]
wirdargv[argc-1]
, 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 Zeichenfolge analysieren, um den Namen der ausführbaren Datei aus dem Pfad zu extrahieren.
/proc/self/path/a.out
Symlink kann unter Solaris 10 und höher verwendet werden.
GetModuleFileNameW
sollte verwendet werden, um einen beliebigen Pfad abrufen zu können, aber nur das Vorhandensein des Codes ist eine gute Anleitung).
Diese Seite besagt:
Das Element argv [0] enthält normalerweise den Namen des Programms, aber darauf sollte man sich nicht verlassen - es ist sowieso ungewöhnlich, dass ein Programm seinen eigenen Namen nicht kennt!
Andere Seiten scheinen jedoch die Tatsache zu belegen, dass es sich immer um den Namen der ausführbaren Datei handelt. Dieser besagt:
Sie werden feststellen, dass argv [0] der Pfad und der Name des Programms selbst ist. Dadurch kann das Programm Informationen über sich selbst ermitteln. Außerdem wird dem Array der Programmargumente ein weiteres hinzugefügt. Ein häufiger Fehler beim Abrufen von Befehlszeilenargumenten besteht darin, argv [0] abzurufen, wenn Sie argv [1] möchten.
argv[0]="-/bin/sh"
? Das ist jedenfalls bei allen Maschinen der Fall, die ich benutzt habe.
Anwendungen mit argv[0] !=
ausführbarem Namen
Viele Shells bestimmen durch Überprüfen, ob es sich um eine Login-Shell handelt argv[0][0] == '-'
. Login-Shells haben unterschiedliche Eigenschaften, insbesondere, dass sie einige Standarddateien wie z /etc/profile
.
Es ist in der Regel der Init selbst oder getty
der führende -
, siehe auch: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152
Binärdateien mit mehreren Anrufen, vielleicht vor allem Busybox . Diese verknüpfen mehrere Namen, z. B. /bin/sh
und /bin/ls
mit einer einzelnen Exebutable /bin/busybox
, die erkennt, von welchem Tool aus sie verwendet werden sollen argv[0]
.
Dies ermöglicht es, eine einzige kleine statisch verknüpfte ausführbare Datei zu haben, die mehrere Tools darstellt und grundsätzlich in jeder Linux-Umgebung funktioniert.
Siehe auch: /unix/315812/why-does-argv-include-the-program-name/315817
Ausführbares POSIX- execve
Beispiel mit argv[0] !=
ausführbarem Namen
Andere erwähnten exec
, aber hier ist ein lauffähiges Beispiel.
ac
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
bc
#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 unter Ubuntu 16.10.
Ich bin mir nicht sicher, ob es sich um eine nahezu universelle Konvention oder einen Standard handelt, aber so oder so sollten Sie sich daran halten. Ich habe es jedoch noch nie außerhalb von Unix- und Unix-ähnlichen Systemen ausgenutzt gesehen. In Unix-Umgebungen - und möglicherweise besonders in früheren Zeiten - können Programme je nach dem Namen, unter dem sie aufgerufen werden, erheblich unterschiedliche Verhaltensweisen aufweisen.
BEARBEITET: Ich sehe aus anderen Posts zur gleichen Zeit wie ich, dass jemand festgestellt hat, dass es von einem bestimmten Standard stammt, aber ich bin sicher, dass die Konvention lange vor dem Standard liegt.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Der Name der ausführbaren Datei steht in keinem Zusammenhang mit dem Wert inargv[0]
.