Lorsque vous passez l'argument à main()
dans une application C ou C ++, sera argv[0]
toujours le nom de l'exécutable? Ou est-ce juste une convention commune et n'est pas garantie d'être vraie à 100%?
Lorsque vous passez l'argument à main()
dans une application C ou C ++, sera argv[0]
toujours le nom de l'exécutable? Ou est-ce juste une convention commune et n'est pas garantie d'être vraie à 100%?
Réponses:
Les devinettes (même les devinettes éclairées) sont amusantes, mais vous devez vraiment consulter les documents de normes pour en être sûr. Par exemple, l'ISO C11 déclare (je souligne):
Si la valeur de
argc
est 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 à partir de l'environnement hôte.
Donc non, ce n'est que le nom du programme si ce nom est disponible. Et ce « représente » le nom du programme, pas nécessairement est le nom du programme. La section précédente indique:
Si la valeur de
argc
est supérieure à zéro, les membres du tableauargv[0]
àargv[argc-1]
inclusif doivent contenir des pointeurs vers des chaînes, qui reçoivent des valeurs définies par l'implémentation par l'environnement hôte avant le démarrage du programme.
Ceci est inchangé par rapport à C99, la norme précédente, et signifie que même les valeurs ne sont pas dictées par la norme - cela dépend entièrement de la mise en œuvre.
Cela signifie que le nom du programme peut être vide si l'environnement hôte ne fournit, et toute autre chose si l'environnement d'accueil ne lui fournir, à condition que « tout autre » représente en quelque sorte le nom du programme. Dans mes moments les plus sadiques, j'envisagerais de le traduire en swahili, de le faire passer par un chiffrement de substitution puis de le stocker dans l'ordre inverse des octets :-).
, La mise en œuvre défini mais n'avoir un sens spécifique dans les normes ISO - le document de mise en œuvre doit comment cela fonctionne. Ainsi, même UNIX, qui peut mettre tout ce qu'il veut avec la famille d'appels, doit (et le fait) le documenter.argv[0]
exec
argv[0]
est à propos de la programmation dans le monde réel.
Sous *nix
les systèmes de type avec exec*()
appels, argv[0]
sera ce que l'appelant met dans la argv0
tache dans l' exec*()
appel.
Le shell utilise la convention selon laquelle c'est le nom du programme, et la plupart des autres programmes suivent la même convention, donc argv[0]
généralement le nom du programme.
Mais un programme Unix non autorisé peut appeler exec()
et faire argv[0]
tout ce qu'il veut, donc peu importe ce que dit le standard C, vous ne pouvez pas compter sur cela à 100%.
Selon la norme C ++, section 3.6.1:
argv [0] doit être le pointeur vers le caractère initial d'un NTMBS qui représente le nom utilisé pour appeler le programme ou ""
Donc non, ce n'est pas garanti, du moins par la norme.
L'ISO-CEI 9899 déclare:
5.1.2.2.1 Démarrage du programme
Si la valeur de
argc
est 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 à partir de l'environnement hôte. Si la valeur deargc
est supérieure à un, les chaînes pointées parargv[1]
throughargv[argc-1]
représentent les paramètres du programme .
J'ai également 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) */
Et puis il vous suffit d'analyser la chaîne pour extraire le nom de l'exécutable du chemin.
/proc/self/path/a.out
lien symbolique peut être utilisable sur Solaris 10 et plus.
GetModuleFileNameW
devrait être utilisé pour pouvoir récupérer n'importe quel chemin, mais juste la présence du code constitue un bon guide).
Applications d'avoir argv[0] !=
un nom d'exécutable
de nombreux shells déterminent s'il s'agit d'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 fournissent certains fichiers par défaut tels que /etc/profile
.
C'est typiquement l'init lui-même ou getty
qui ajoute le premier -
, voir aussi: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152
binaires multi-appels, peut-être plus particulièrement Busybox . Ces liens symbolisent plusieurs noms, par exemple /bin/sh
et /bin/ls
vers un seul exécutable /bin/busybox
, qui reconnaît l'outil à utiliser 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: /unix/315812/why-does-argv-include-the-program-name/315817
Runnable Posix execve
exemple où le argv[0] !=
nom exécutable
D'autres ont mentionné exec
, mais voici un exemple exécutable.
ac
#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.
Cette page indique:
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 pour un programme de ne pas connaître 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 en ajoute également un de plus au tableau des arguments du programme, donc une erreur courante lors de la récupération des arguments de ligne de commande est de saisir argv [0] lorsque vous voulez argv [1].
argv[0]="-/bin/sh"
? C'est le cas sur toutes les machines que j'ai utilisées, de toute façon.
Je ne sais pas s'il s'agit d'une convention ou d'une norme presque universelle, mais dans tous les cas, vous devriez la respecter. Cependant, je ne l'ai jamais vu exploité en dehors des systèmes Unix et de type Unix. Dans les environnements Unix - et peut-être en particulier 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 articles en même temps que le mien que quelqu'un l'a identifié comme venant d'une norme particulière, mais je suis sûr que la convention est antérieure à la norme.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Le nom de l'exécutable n'a aucun rapport avec la valeur dansargv[0]
.