132 Stimmen

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

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?

148voto

paxdiablo Punkte 809679

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 mit argv[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 Arrayelemente argv[0] über argv[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).

56voto

Richard Pennington Punkte 19289

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.

9voto

Gemäß dem C++ Standard, Abschnitt 3.6.1:

argv[0] ist der Zeiger auf die Datei Anfangszeichen einer NTMBS, die den Namen darstellt, der zum Aufruf des Programm oder ""

Also nein, es ist nicht garantiert, zumindest nicht durch den Standard.

8voto

Gregory Pakosz Punkte 66918

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 mit argv[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 von argc größer als eins ist, werden die Zeichenketten, auf die mit argv[1] über argv[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.

6voto

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 aus argv[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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X