Pourquoi argv inclut-il le nom du programme?

Les programmes Unix / Linux typiques acceptent les entrées de ligne de commande comme un nombre darguments (int argc) et un vecteur darguments (char *argv[]). Le premier élément de argv est le nom du programme – suivi des arguments réels.

Pourquoi le nom du programme est-il passé à lexécutable comme argument? Existe-t-il des exemples de programmes utilisant leur propre nom (peut-être une sorte de situation exec)?

Commentaires

  • comme mv et cp?
  • Sur Debian sh est un lien symbolique vers dash. Ils se comportent différemment, lorsquils sont appelés comme sh ou comme dash
  • @AlexejMagura Si vous utilisez quelque chose comme busybox (courant sur les disques de secours et autres), alors à peu près tout (cp, mv, rm, ls, …) est un lien symbolique vers busybox.
  • Je ‘ trouve cela vraiment difficile à ignorer, donc je ‘ ll dites-le: vous voulez probablement dire  » GNU  » programmes (gcc, bash, gunzip, la plupart du reste du système dexploitation …), car Linux nest que le noyau.
  • @ wizzwizz4 Quel est le problème de ‘ avec  » Programmes Unix / Linux typiques « ? Je lai lu comme  » Programmes typiques fonctionnant sous Unix / Linux « . Cela ‘ est bien meilleur que votre restriction à certains programmes GNU. Dennis Ritchie nutilisait certainement aucun programme GNU. BTW le noyau Hurd est un exemple de programme GNU qui na pas de fonction principale …

Answer

Pour commencer, notez que argv[0] nest pas nécessairement le nom du programme. Cest ce que lappelant met dans argv[0] de lappel système execve (par exemple, voir cette question sur Stack Overflow ). (Toutes les autres variantes de exec ne sont pas des appels système mais des interfaces vers execve.)

Supposons, par exemple, ce qui suit (en utilisant execl):

execl("/var/tmp/mybackdoor", "top", NULL); 

/var/tmp/mybackdoor est ce qui est exécuté mais argv[0] est défini sur top, et voici ce que ps ou ( le réel) top safficherait. Pour en savoir plus, consultez cette réponse sur U & L SE.

Définition de tout ceci mis à part: avant lavènement des systèmes de fichiers sophistiqués comme /proc, argv[0] était le seul moyen pour un processus de connaître son propre nom. À quoi cela servirait-il?

  • Plusieurs programmes personnalisent leur comportement en fonction du nom par lequel ils ont été appelés (généralement par des liens symboliques ou physiques, par exemple Utilitaires de BusyBox ; plusieurs autres exemples sont fournis dans dautres réponses à cette question).
  • De plus, les services, les démons et autres programmes qui se connectent via syslog ajoutent souvent leur nom au entrées de journal; sans cela, le suivi des événements deviendrait presque impossible.

Commentaires

  • Des exemples de tels programmes sont bunzip2, bzcat et bzip2, pour lesquels les deux premiers sont des liens symboliques vers le troisième.
  • @Ruslan Intéressant zcat nest pas un lien symbolique. Ils semblent éviter les inconvénients de cette technique en utilisant un script shell à la place. Mais ils ne parviennent pas à afficher un car quelquun qui a ajouté des options à gzip a oublié de faire main tain zcat aussi.
  • Aussi loin que je me souvienne, les normes de codage GNU ont découragé lutilisation de argv [0] pour changer le comportement du programme (section  » Normes pour les interfaces en général  » dans la version actuelle ). gunzip est une exception historique.
  • busybox est un autre excellent exemple. Il peut être appelé par 308 noms différents pour appeler différentes commandes: busybox.net/downloads/BusyBox.html#commands
  • Beaucoup, beaucoup dautres programmes injectent également leur argv[0] dans leur sortie dutilisation / daide au lieu de coder en dur leur nom. Certains en entier, certains juste le nom de base.

Réponse

Beaucoup:

  • Bash sexécute en mode POSIX lorsque argv[0] est sh. Il fonctionne comme un shell de connexion lorsque argv[0] commence par -.
  • Vim se comporte différemment lorsquil est exécuté en tant que vi, view, evim, eview, ex, vimdiff, etc.
  • Busybox, comme déjà mentionné.
  • Dans les systèmes avec systemd comme init, shutdown, reboot, etc. sont liens symboliques vers systemctl .
  • et ainsi de suite.

Commentaires

  • Un autre est sendmail et mail. Chaque MTA Unix est livré avec un lien symbolique pour ces deux commandes, et est conçu pour émuler le comportement original de ‘ lorsquil est appelé comme tel, ce qui signifie que tout programme Unix qui a besoin denvoyer du courrier sait exactement comment ils peuvent le faire.
  • un autre cas courant: test et [: lorsque vous appelez lancien , il gère une erreur si le dernier argument est ]. (sur Debian stable, ces commandes sont deux programmes différents, mais les versions précédentes et MacO utilisent toujours le même programme). Et tex, latex et ainsi de suite: le binaire est le même, mais en regardant comment il a été appelé, il choisit le bon fichier de configuration . init est similaire.
  • Associé, [ considère quil sagit dune erreur si le dernier argument est pas ].
  • Je suppose que cela répond à la deuxième question, mais pas à la première. Je doute fort quun concepteur dOS se soit assis et ait dit » Hé, ce serait cool si javais le même programme faisant des choses différentes juste en fonction de son nom dexécutable. Je suppose que je ‘ je vais inclure le nom dans son tableau darguments, alors. «
  • @Joey Oui, le la formulation vise à indiquer que (Q:  » Y a-t-il des …?  » A:  » Plenty: … « )

Réponse

Historiquement, argv nest quun tableau de pointeurs vers les « mots » de la ligne de commande, il est donc logique de commencer par le premier « mot », qui se trouve être le nom du programme.

Et il y a pas mal de programmes qui se comportent différemment selon le nom utilisé pour les appeler, donc vous pouvez simplement créer différents liens vers eux et obtenir différentes « commandes ». Lexemple le plus extrême auquel je puisse penser est busybox , qui agit comme plusieurs douzaines de « commandes » différentes selon la façon dont il sappelle .

Modifier

: Références pour la 1ère édition Unix, comme demandé

On peut voir par exemple de la fonction principale de cc que argc et argv étaient déjà utilisés. Le shell copie les arguments dans le parbuf dans la partie newarg de la boucle, tout en traitant la commande elle-même de la même manière que les arguments. (Bien sûr, plus tard, il nexécute que le premier argument, qui est le nom de la commande). Cela ressemble à execv et les parents nexistaient pas alors.

Commentaires

  • veuillez ajouter des références qui sauvegardez ceci.
  • Dun rapide survol, exec prend le nom de la commande à exécuter et un tableau de pointeurs de caractères terminé par zéro (mieux vu à minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , où exec prend des références au libellé 2 et au libellé 1, et au libellé 2: apparaît etc/init\0 et au libellé 1: apparaît une référence à létiquette 2 et un zéro de fin), ce qui correspond essentiellement à ce que execve fait aujourdhui moins envp.
  • execv et execl ont existé  » pour toujours  » (cest-à-dire depuis le début des années 1970) – execv était un appel système et était une fonction de bibliothèque qui lappelait. execve nexistait pas ‘ car lenvironnement nexistait pas ‘ alors. Les autres membres de la famille ont été ajoutés plus tard.
  • @ G-Man Pouvez-vous mindiquer execv dans la source v1 que jai liée? Curieux.

Réponse

Cas dutilisation:

Vous pouvez utiliser le nom du programme pour modifier le comportement du programme .

Par exemple, vous pouvez créer des liens symboliques vers le binaire réel.

Un exemple célèbre où cette technique est utilisée est le projet busybox qui installe un seul binaire et de nombreux liens symboliques vers lui. (ls, cp, mv, etc.). Ils le font pour économiser de lespace de stockage parce que leurs cibles sont de petits appareils intégrés.

Cest aussi utilisé dans setarch depuis util-linux:

$ ls -l /usr/bin/ | grep setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 i386 -> setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux32 -> setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux64 -> setarch -rwxr-xr-x 1 root root 14680 2015-10-22 16:54 setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 x86_64 -> setarch 

Ici, ils utilisent essentiellement cette technique pour éviter de nombreux fichiers source en double ou simplement pour garder les sources plus lisibles.

Un autre cas dutilisation serait un programme qui a besoin pour charger certains modules ou données lors de lexécution. Le fait davoir le chemin du programme vous permet de charger des modules à partir dun chemin relatif à lemplacement du programme .

De plus, de nombreux programmes affichent des messages derreur incluant le nom du programme .

Pourquoi :

  1. Parce que cest la convention POSIX ( man 3p execve):

argv est un tableau de chaînes darguments transmis au nouveau programme. Par convention, la première de ces chaînes doit contenir le nom de fichier associé au fichier en cours dexécution.

  1. Cest C standard (au moins C99 et C11):

Si la valeur de argc est supérieure à zéro, la chaîne pointée par argv [0 ] représente le nom du programme; argv [0] [0] doit être le caractère nul si le nom du programme nest pas disponible depuis lenvironnement hôte.

Notez que le standard C dit « programme name « pas » filename « .

Commentaires

  • Na pas ‘ cette pause si vous atteignez le lien symbolique dun autre lien symbolique?
  • @Mehrdad, Oui que ‘ est linconvénient et peut être déroutant pour lutilisateur.
  • @rudimeier: Vos ‘ Pourquoi ‘ éléments ne sont pas vraiment des raisons, ils ‘ ne sont quun  » homunculus « , cest-à-dire que cela soulève simplement la question de savoir pourquoi la norme exige que ce soit le cas.
  • @ La question de einpoklum OP ‘ était: Pourquoi le nom du programme est-il passé à lexécutable? Jai répondu: Parce que le standard POSIX et C nous dit de le faire. Comment pensez-vous que ‘ nest pas vraiment une raison ? Si les documents que jai ‘ cités nexistaient pas, alors de nombreux programmes ne passeraient probablement pas le nom du programme.
  • LOP demande effectivement  » POURQUOI les standards POSIX et C disent-ils de faire cela?  » Certes, le libellé était à un niveau abstrait, mais cela semble clair. De manière réaliste, le seul moyen de savoir est de demander aux auteurs.

Réponse

En plus des programmes modifiant leur comportement selon la manière dont ils ont été appelés, je trouve argv[0] utile pour imprimer lutilisation dun programme, comme ceci:

printf("Usage: %s [arguments]\n", argv[0]); 

Cela fait que le message dutilisation utilise toujours le nom par lequel il a été appelé. Si le programme est renommé, son message dutilisation change avec lui. Il inclut même le nom du chemin avec lequel il a été appelé:

# cat foo.c #include <stdio.h> int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); } # gcc -Wall -o foo foo.c # mv foo /usr/bin # cd /usr/bin # ln -s foo bar # foo Usage: foo [arguments] # bar Usage: bar [arguments] # ./foo Usage: ./foo [arguments] # /usr/bin/foo Usage: /usr/bin/foo [arguments] 

Cest une bonne idée, en particulier pour les petits outils / scripts spéciaux qui pourraient vivre partout

Cela semble aussi une pratique courante dans les outils GNU, voir ls par exemple:

% ls --qq ls: unrecognized option "--qq" Try "ls --help" for more information. % /bin/ls --qq /bin/ls: unrecognized option "--qq" Try "/bin/ls --help" for more information. 

Commentaires

  • +1. Jallais suggérer la même chose. Cest étrange que tant de gens se concentrent sur le changement de comportement et ne mentionnent probablement pas le plus évident et usage beaucoup plus répandu.

Réponse

On exécute le programme en tapant: program_name0 arg1 arg2 arg3 ....

Donc, le shell devrait déjà diviser le jeton, et le premier jeton est déjà le nom du programme. Et BTW donc il y a les mêmes indices côté programme et sur shell.

Je pense que cétait juste une astuce de commodité (au tout début), et, comme vous le voyez dans dautres réponses, cétait également très pratique, donc cette tradition a été poursuivie et s et comme API.

Réponse

Fondamentalement, argv inclut le nom du programme afin que vous puissiez écrire des messages derreur comme prgm: file: No such file or directory, qui serait implémenté avec quelque chose comme ceci:

 fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] ); 

Réponse

Un autre exemple dapplication de ceci est ce programme, qui se remplace par … lui-même, jusquà ce que vous tapiez quelque chose qui nest pas « t y.

#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main (int argc, char** argv) { (void) argc; printf("arg: %s\n", argv[1]); int count = atoi(argv[1]); if ( getchar() == "y" ) { ++count; char buf[20]; sprintf(buf, "%d", count); char* newargv[3]; newargv[0] = argv[0]; newargv[1] = buf; newargv[2] = NULL; execve(argv[0], newargv, NULL); } return count; } 

Évidemment, une sorte dexemple artificiel mais intéressant, mais je pense que cela peut avoir de réelles utilisations – par exemple, un binaire à mise à jour automatique, qui réécrit son propre espace mémoire avec une nouvelle version de lui-même quil a téléchargée ou modifiée.

Exemple:

$ ./res 1 arg: 1 y arg: 2 y arg: 3 y arg: 4 y arg: 5 y arg: 6 y arg: 7 n 7 | $ 

Source, et quelques informations supplémentaires .

Commentaires

  • Félicitations pour avoir atteint 1 000.

Réponse

Le chemin daccès au programme est argv[0], afin que le programme puisse récupérer les fichiers de configuration etc. de son répertoire dinstallation.
Ce serait impossible sans argv[0].

Commentaires

  • Ce ‘ nest pas une explication particulièrement bonne – il ny a ‘ aucune raison pour laquelle nous ne pouvions pas ‘ Jai normalisé quelque chose comme (char *path_to_program, char **argv, int argc) par exemple
  • Afaik, la plupart des programmes extraient la configuration à partir dun emplacement standard (~/.<program>, /etc/<program, $XDG_CONFIG_HOME ) et soit prendre un paramètre pour le changer, soit avoir une option de compilation qui transforme une constante en binaire.

Answer

ccache se comporte de cette façon pour imiter différents appels aux binaires du compilateur. ccache est un cache de compilation – le but est de ne jamais compiler le même code source deux fois mais de renvoyer le code objet du cache si possible.

À partir de page de manuel ccache , « il y a deux façons dutiliser ccache. Vous pouvez préfixer vos commandes de compilation avec ccache ou vous pouvez laisser ccache se faire passer pour le compilateur en créant un lien symbolique (nommé comme compilateur) vers ccache. Le La première méthode est la plus pratique si vous voulez simplement essayer ccache ou si vous souhaitez lutiliser pour certains projets spécifiques. La deuxième méthode est plus utile lorsque vous souhaitez utiliser ccache pour toutes vos compilations. « 

Le La méthode symlinks consiste à exécuter ces commandes:

cp ccache /usr/local/bin/ ln -s ccache /usr/local/bin/gcc ln -s ccache /usr/local/bin/g++ ln -s ccache /usr/local/bin/cc ln -s ccache /usr/local/bin/c++ ... etc ... 

… dont leffet est de permettre à ccache de récupérer toutes les commandes qui seraient autrement allées aux compilateurs, permettant ainsi à ccache de renvoyer un fichier mis en cache ou de passer la commande au compilateur réel.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *