web-dev-qa-db-fra.com

"Argv [0] = nom-exécutable" est-il un standard accepté ou juste une convention commune?

Lors du passage de l'argument à main() dans une application C ou C++, argv[0] sera-t-il toujours le nom de l'exécutable? Ou est-ce juste une convention commune et il n'est pas garanti que ce soit vrai 100% du temps? 

88
Mike Willekes

Les devinettes (même les supposées éclairées) sont amusantes, mais vous devez vraiment consulter les documents de normes pour être sûr. Par exemple, ISO C11 dit (je souligne):

Si la valeur de argc est supérieure à zéro, la chaîne indiquée par argv[0] représente le nom du programme; argv[0][0] doit être un caractère nul si le nom du programme n'est pas disponible dans l'environnement hôte.

Donc non, c'est seulement le nom du programme si ce nom est disponible. Et cela "représente" le nom du programme, pas nécessairement est le nom du programme. La section précédente qui dit:

Si la valeur de argc est supérieure à zéro, les membres du tableau argv[0] à argv[argc-1] inclus doivent contenir des pointeurs sur des chaînes, auxquelles l'environnement hôte donne des valeurs définies par l'implémentation avant le démarrage du programme. .

Ceci est inchangé par rapport à C99, le standard précédent, et signifie que même les valeurs ne sont pas dictées par le standard - cela dépend entièrement de la mise en œuvre.

Cela signifie que le nom du programme peut être vide si l'environnement de l'hôte ne le fournit pas , et rien d'autre si l'environnement de l'hôte Le le fournit, à condition que "tout le reste" représente le nom du programme. Dans mes moments les plus sadiques, je voudrais envisager de le traduire en swahili, de l'exécuter à l'aide d'un chiffrement de substitution, puis de le stocker dans l'ordre inverse des octets :-).

Toutefois, les définitions définies par l'implémentation ont une signification spécifique dans les normes ISO - l'implémentation doit documenter son fonctionnement. Ainsi, même UNIX, qui peut mettre tout ce qu'il veut dans argv[0] avec la famille d'appels exec, doit le documenter (et le fait).

105
paxdiablo

Dans les systèmes de type *nix avec des appels exec*(), argv[0] sera ce que l'appelant mettra à l'emplacement argv0 dans l'appel exec*().

Le shell utilise la convention selon laquelle il s’agit du nom du programme, et la plupart des autres programmes suivent la même convention, donc argv[0] correspond généralement au nom du programme.

Mais un programme Unix non fiable peut appeler exec() et transformer argv[0] à sa guise. Ainsi, peu importe ce que dit le standard C, vous ne pouvez pas compter sur cela 100% du temps.

45
Richard Pennington

Selon la norme C++, section 3.6.1:

argv [0] sera le pointeur sur le caractère initial d'un NTMBS qui représente le nom utilisé pour invoquer le programme ou ""

Donc non, ce n'est pas garanti, du moins par la norme. 

8
anon

Cette page déclare:

L'élément argv [0] contient normalement le nom du programme, mais il ne faut pas s'y fier. De toute façon, il est inhabituel qu'un programme ne connaisse pas son propre nom!

Cependant, d'autres pages semblent confirmer le fait qu'il s'agit toujours du nom de l'exécutable. Celui-ci déclare:

Vous remarquerez que argv [0] est le chemin et le nom du programme lui-même. Cela permet au programme de découvrir des informations sur lui-même. Il ajoute également un élément supplémentaire au tableau des arguments du programme. Par conséquent, une erreur courante lors de l'extraction des arguments de ligne de commande consiste à récupérer argv [0] lorsque vous voulez argv [1].

4
ChrisF

ISO-CEI 9899 stipule:

5.1.2.2.1 Démarrage du programme

Si la valeur deargcest supérieure à zéro, la chaîne pointée parargv[0]représente le nom du programme;argv[0][0]doit être le caractère nul si le nom du programme n'est pas disponible dans l'environnement hôte. Si la valeur deargcest supérieure à un, les chaînes pointées parargv[1]àargv[argc-1]représentent le paramètres du programme _.

J'ai aussi utilisé:

#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) */

Ensuite, il vous suffit d'analyser la chaîne pour extraire le nom de l'exécutable du chemin.

4
Gregory Pakosz

Applications pour avoir argv[0] != nom exécutable

  • de nombreux shells déterminent s'ils sont un shell de connexion en vérifiant argv[0][0] == '-'. Les shells de connexion ont des propriétés différentes, notamment le fait qu'ils génèrent certains fichiers par défaut tels que /etc/profile.

    C'est généralement l'init lui-même ou la getty qui ajoute le - principal, voir aussi: https://unix.stackexchange.com/questions/299408/how-to-login-automatiquement- avec-typing-le-root-nom -or-password-in-build/300152 # 300152

  • binaires multi-appels, peut-être surtout/ Busybox . Ces liens symboliques multiples, p. Ex. /bin/sh et /bin/ls en un /bin/busybox unique, qui reconnaît quel outil utiliser à partir de argv[0].

    Cela permet d’avoir un seul petit exécutable lié statiquement qui représente plusieurs outils et fonctionnera essentiellement sur n’importe quel environnement Linux.

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

Runnable POSIX execve exemple où argv[0] != nom exécutable 

D'autres ont mentionné exec, mais voici un exemple exécutable.

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);
}

avant JC

#include <stdio.h>

int main(int argc, char **argv) {
    puts(argv[0]);
}

Ensuite:

gcc a.c -o a.out
gcc b.c -o b.out
./a.out

Donne:

yada yada

Oui, argv[0] pourrait aussi être:

Testé sur Ubuntu 16.10.

Je ne suis pas sûr qu'il s'agisse d'une convention ou d'une norme quasi universelle, mais vous devez respecter cette règle. Je ne l'ai jamais vue exploitée en dehors des systèmes Unix et de type Unix, cependant. Dans les environnements Unix - et peut-être particulièrement dans l'ancien temps -, les programmes peuvent avoir des comportements très différents selon le nom sous lequel ils sont appelés.

EDITED: Je vois dans d’autres publications en même temps que la mienne que quelqu'un a identifié cela comme provenant d’une norme particulière, mais je suis sûr que la convention est antérieure à la norme.

2
Joe Mabel

Si vous démarrez un programme Amiga par Workbench, argv [0] ne sera pas défini, mais uniquement par CLI.

0
Polluks