it-swarm.com.de

Ist "argv [0] = ausführbare Datei" ein akzeptierter Standard oder nur eine gängige Konvention?

Wird argv[0] bei der Übergabe eines Arguments an main() in einer C- oder C++ - Anwendung immer der Name der ausführbaren Datei sein? Oder ist dies nur eine gängige Konvention und es kann nicht garantiert werden, dass sie zu 100% wahr ist? 

88
Mike Willekes

Vermutungen (sogar fundierte Vermutungen) machen Spaß, aber Sie müssen sich unbedingt an die Normungsdokumente wenden, um sicher zu gehen. Zum Beispiel heißt es in ISO C11 (meine Betonung):

Wenn der Wert von argc größer als Null ist, wird die Zeichenfolge, auf die mit argv[0]repräsentiert der Programmname verweist. argv[0][0] ist das Nullzeichen, wenn der Programmname nicht in der Host-Umgebung verfügbar ist.

Also nein, es ist nur der Programmname, wenn dieser Name verfügbar. Ist und "repräsentiert" den Programmnamen, nicht notwendigerweise ist den Programmnamen. Der Abschnitt davor lautet:

Wenn der Wert von argc größer als Null ist, müssen die Arraymitglieder argv[0] bis einschließlich argv[argc-1] Zeiger auf Zeichenfolgen enthalten, die vor dem Programmstart von der Host-Umgebung implementierungsdefinierte Werte erhalten.

Dies ist gegenüber C99 (dem vorherigen Standard) unverändert und bedeutet, dass auch die Werte nicht durch den Standard vorgegeben werden - es liegt allein an der Implementierung.

Dies bedeutet, dass der Programmname leer sein kann, wenn die Host-Umgebung nicht ihn bereitstellt, und alles andere, wenn die Host-Umgebung tut es vorsieht, vorausgesetzt, "irgendetwas anderes" repräsentiert dies irgendwie der Programmname In meinen sadistischeren Momenten würde ich es in Betracht ziehen, es in Swahili zu übersetzen, es durch eine Substitutions-Chiffre laufen zu lassen und es dann in umgekehrter Byte-Reihenfolge zu speichern :-).

Durch die Implementierung definierte does haben jedoch eine bestimmte Bedeutung in den ISO-Standards - die Implementierung muss dokumentieren, wie sie funktioniert. Also muss auch UNIX, das alles, was es möchte, mit der exec-Familie von Anrufen in argv[0] stecken kann, dies dokumentieren.

101
paxdiablo

Unter *nix-Typsystemen mit exec*()-Aufrufen ist argv[0] das, was der Anrufer in den argv0-Bereich des exec*()-Aufrufs gibt.

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 ungezogenes Unix-Programm kann exec() aufrufen und argv[0] beliebig machen, also kann man, egal was der C-Standard sagt, nicht zu 100% auf diese Zeit zählen.

45

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

argv [0] ist der Zeiger auf die Anfangszeichen eines NTMBS, das steht für den Namen, der zum Aufrufen von .__ verwendet wird. Programm oder ""

Also nein, es wird zumindest vom Standard nicht garantiert. 

8
anon

Diese Seite besagt:

Das Element argv [0] enthält normalerweise den Namen des Programms, aber darauf sollte man sich nicht verlassen - ohnehin ist es ungewöhnlich, dass ein Programm seinen eigenen Namen nicht kennt!

Andere Seiten scheinen jedoch die Tatsache zu unterstützen, dass es sich immer um den Namen der ausführbaren Datei handelt. Dieses hier besagt:

Sie werden feststellen, dass argv [0] der Pfad und der Name des Programms selbst ist. Dadurch kann das Programm Informationen über sich selbst finden. Es fügt dem Array der Programmargumente außerdem noch eine weitere hinzu. Wenn Sie Befehlszeilenargumente abrufen, müssen Sie argv [0] abrufen, wenn Sie argv [1] möchten.

4
ChrisF

ISO-IEC 9899 besagt:

5.1.2.2.1 Programmstart

Wenn der Wert vonargcgrößer als Null ist, stellt der String, auf denargv[0]zeigt, den Programmnamen dar;argv[0][0]ist das Nullzeichen, wenn der Programmname nicht in der Host-Umgebung verfügbar ist. Wenn der Wert vonargcgrößer als 1 ist, repräsentieren die Zeichenfolgen, auf die durchargv[1]durchargv[argc-1]verwiesen wird, die Programmparameter.

Ich habe auch benutzt:

#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 die Zeichenfolge analysieren, um den Namen der ausführbaren Datei aus dem Pfad zu extrahieren.

4
Gregory Pakosz

Anwendungen mit argv[0] != Name der ausführbaren Datei

  • viele Shells bestimmen, ob es sich um eine Login-Shell handelt, indem sie argv[0][0] == '-' überprüfen. Login-Shells haben unterschiedliche Eigenschaften, insbesondere, dass sie einige Standarddateien wie /etc/profile als Quelle verwenden.

    Normalerweise fügt der init selbst oder getty den führenden - hinzu, siehe auch: https://unix.stackexchange.com/questions/299408/how-to-login-automatisch- ohne-die-wurzelnutzername -oder-Passwort-in-build/300152 # 300152

  • multi-Call-Binaries, vielleicht am bemerkenswertesten Busybox . Diese symbolischen Mehrfachnamen, z. /bin/sh und /bin/ls zu einer einzigen ausführbaren Datei /bin/busybox, die das von argv[0] zu verwendende Werkzeug erkennt.

    Dadurch ist es möglich, eine kleine statisch verknüpfte ausführbare Datei zu haben, die mehrere Tools darstellt und grundsätzlich in jeder Linux-Umgebung ausgeführt werden kann.

Siehe auch: https://unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817

Runable POSIX execve Beispiel, wo 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.

Ich bin mir nicht sicher, ob es eine fast universelle Konvention oder ein Standard ist, aber auf jeden Fall sollten Sie sich daran halten. Ich habe es noch nie außerhalb von Unix und Unix-ähnlichen Systemen ausgenutzt gesehen. In Unix-Umgebungen - und möglicherweise besonders in früheren Zeiten - haben Programme möglicherweise ein unterschiedliches Verhalten, je nachdem, unter welchem ​​Namen sie aufgerufen werden.

BEARBEITET: Ich sehe zur gleichen Zeit aus meinen anderen Beiträgen, dass jemand es als einen bestimmten Standard erkannt hat, aber ich bin mir sicher, dass die Konvention lange vor dem Standard liegt.

2
Joe Mabel

Wenn Sie ein Amiga-Programm über Workbench starten, wird argv [0] nicht nur über die CLI festgelegt.

0
Polluks