Ist "argv [0] = Name der ausführbaren Datei" ein akzeptierter Standard oder nur eine übliche Konvention?

102

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?

Mike Willekes
quelle
19
Beachten Sie unter Unix : execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. Der Name der ausführbaren Datei steht in keinem Zusammenhang mit dem Wert in argv[0].
Jonathan Leffler

Antworten:

118

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 argcgröß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 argcgrößer als Null ist, müssen die Array-Mitglieder argv[0]bis argv[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 execFamilie der Aufrufe alles aufnehmen kann, was es will , muss (und tut) dies dokumentieren.

paxdiablo
quelle
3
Das mag der Standard sein, aber Unix erzwingt ihn einfach nicht und Sie können sich nicht darauf verlassen.
dmckee --- Ex-Moderator Kätzchen
4
Die Frage nicht erwähnt UNIX überhaupt . Es war schlicht und einfach eine C-Frage, daher ist ISO C das Referenzdokument. Der Programmname ist eine im Standard definierte Implementierung, sodass eine Implementierung frei tun kann, was sie will, einschließlich des Zulassens von etwas, das nicht der tatsächliche Name ist - ich dachte, ich hätte dies im vorletzten Satz klargestellt.
Paxdiablo
2
Pax, ich habe dich nicht abgewählt und diejenigen, die das getan haben, nicht gutgeheißen, weil diese Antwort so maßgeblich ist, wie es nur geht . Aber ich denke, die Unzuverlässigkeit des Wertes von argv[0]ist für die Programmierung in der realen Welt von Vorteil.
dmckee --- Ex-Moderator Kätzchen
4
@caf, das stimmt. Ich habe gesehen, dass es so unterschiedliche Dinge enthält wie den vollständigen Pfad des Programms ('/ progpath / prog'), nur den Dateinamen ('prog'), einen leicht modifizierten Namen ('-prog'), einen beschreibenden Namen (') prog - ein Programm zum Proggen ') und nichts (' '). Die Implementierung muss definieren, was sie enthält, aber das ist alles, was der Standard erfordert.
Paxdiablo
3
Vielen Dank an alle! Tolle Diskussion aus einer (scheinbar) einfachen Frage. Obwohl Richards Antwort für * nix-Betriebssysteme gültig ist, habe ich die Antwort von paxdiablo ausgewählt, weil ich weniger am Verhalten eines bestimmten Betriebssystems interessiert bin und hauptsächlich an der Existenz (oder Abwesenheit) eines akzeptierten Standards. (Wenn Sie neugierig sind: Im Kontext der ursprünglichen Frage - Ich habe kein Betriebssystem. Ich schreibe Code, um den unformatierten argc / argv-Puffer für eine ausführbare Datei zu erstellen, die auf ein eingebettetes Gerät geladen ist und wissen muss, was ich tun soll mit argv [0]). +1 für StackOverflow, weil du großartig bist!
Mike Willekes
48

Unter *nixTypsysteme mit exec*()Anrufen argv[0]wird alles angezeigt, was der Anrufer an die argv0Stelle 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.

Richard Pennington
quelle
4
Dies ist eine bessere Antwort als die oben genannten von paxdiablo. Der Standard nennt es nur den "Programmnamen", aber dies wird meines Wissens nirgendwo durchgesetzt. Unix-Kernel übergeben die an execve () übergebene Zeichenfolge einheitlich unverändert an den untergeordneten Prozess.
Andy Ross
4
Der C-Standard ist in seinen Aussagen begrenzt, da er nichts über 'execve ()' usw. weiß. Der POSIX-Standard ( opengroup.org/onlinepubs/9699919799/functions/execve.html ) hat mehr zu sagen - um es klar zu machen Das, was in argv [0] steht, ist nach Lust und Laune des Prozesses, der den Systemaufruf 'execve ()' (oder einen verwandten) ausführt.
Jonathan Leffler
1
@Andy, du kannst deine Meinung frei haben :-) Aber du liegst falsch in Bezug auf die Durchsetzung. Wenn eine Implementierung nicht dem Standard entspricht, ist sie nicht konform. Und in der Tat, da es die Implementierung definiert, was die „Programmname“ ist, ein Betriebssystem wie UNIX ist konform, solange es gibt , was der Name ist. Dazu gehört, dass Sie einen Programmnamen offensichtlich vortäuschen können, indem Sie argv [0] mit allem laden, was Sie in der exec-Aufruffamilie wollen.
Paxdiablo
Das ist das Schöne am Wort "repräsentiert" im Standard, wenn es sich auf argv [0] ("es repräsentiert den Programmnamen") und argv [1..N] ("sie repräsentieren die Programmargumente") bezieht. "unbeladene Schwalbe" ist ein gültiger Programmname.
Richard Pennington
8

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.


quelle
5
Ich nehme an, das ist eine nullterminierte Multi-Byte-Zeichenfolge.
Paxdiablo
5

In ISO-IEC 9899 heißt es:

5.1.2.2.1 Programmstart

Wenn der Wert von argcgröß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 von argcgrößer als eins ist, repräsentieren die Zeichenfolgen, auf die durch gezeigt argv[1]wird argv[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.

Gregory Pakosz
quelle
2
Der /proc/self/path/a.outSymlink kann unter Solaris 10 und höher verwendet werden.
Ephemient
Upvoted für den Code (nicht gesagt, dass er ideal oder korrekt ist, z. B. unter Windows GetModuleFileNameWsollte verwendet werden, um einen beliebigen Pfad abrufen zu können, aber nur das Vorhandensein des Codes ist eine gute Anleitung).
Prost und hth. - Alf
4

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.

ChrisF
quelle
11
Einige Programme nutzen die Tatsache, dass sie den Namen nicht kennen, mit dem sie aufgerufen wurden. Ich glaube, BusyBox ( Busybox.net/about.html ) funktioniert so. Es gibt nur eine ausführbare Datei, die viele verschiedene Befehlszeilenprogramme implementiert. Es verwendet eine Reihe von symbolischen Links und argv [0], um zu bestimmen, welches Befehlszeilentool ausgeführt werden soll
Trent
Ja, ich erinnere mich, dass ich bemerkt habe, dass "gunzip" eine symbolische Verbindung zu "gzip" war, und mich für einen Moment gefragt habe, wie das funktioniert hat.
David Thornley
2
Viele Programme suchen in argv [0] nach Informationen. Wenn beispielsweise die letzte Komponente des Namens mit einem Bindestrich beginnt (z. B. '/ bin / -sh'), führt die Shell das Profil und andere Elemente wie bei einer Anmeldeshell aus.
Jonathan Leffler
2
@ Jon: Ich dachte, Login-Shells wurden mit gestartet argv[0]="-/bin/sh"? Das ist jedenfalls bei allen Maschinen der Fall, die ich benutzt habe.
Ephemient
3

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 gettyder 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/shund /bin/lsmit 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- execveBeispiel 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.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
2

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.

Joe Mabel
quelle
1
Ich wünschte wirklich, wenn die Leute meine Antwort "notieren" würden, würden sie einen Hinweis geben, was sie daran nicht mögen.
Joe Mabel
0

Wenn Sie ein Amiga-Programm mit Workbench starten, wird argv [0] nicht festgelegt, sondern nur mit CLI.

Polluks
quelle