Trouver le chemin de l'exécutable courant sans / proc/self/exe

il me semble que Linux a la facilité avec /proc/self/exe. Mais j'aimerais savoir s'il y a un moyen pratique de trouver le répertoire de l'application actuelle en c/" class="blnk">C/C++ avec des interfaces multiplateformes. J'ai vu des projets tourner en rond avec argv[0], mais cela ne semble pas entièrement fiable.

si vous aviez eu à supporter, disons, Mac OS X, qui n'a pas /proc/, qu'auriez-vous fait? Utilisez # ifdefs pour isoler le code spécifique à la plate-forme (NSBundle, for exemple)? Ou essayer de déduire le chemin de l'exécutable à partir d'argv[0], $PATH et ainsi de suite, au risque de trouver des bogues dans les cas de bord?

170
demandé sur Jonas 2009-06-21 10:12:48

12 réponses

Certains OS des interfaces spécifiques:

la méthode portable (mais moins fiable) est d'utiliser argv[0] . Bien qu'il puisse être défini à n'importe quoi par le programme d'appel, par convention il est défini à soit un nom de chemin du exécutable ou un nom qui a été trouvé en utilisant $PATH .

quelques shells, dont bash et ksh, positionne la variable d'environnement " _ " sur le chemin complet de l'exécutable avant qu'il ne soit exécuté. Dans ce cas, vous pouvez utiliser getenv("_") pour l'obtenir. Cependant, ce n'est pas fiable car tous les shells ne font pas cela, et il peut être défini à n'importe quoi OU être laissé sur un processus parent qui ne l'a pas modifié avant d'exécuter votre programme.

325
répondu mark4o 2017-05-23 12:34:41

l'utilisation de /proc/self/exe est non portable et peu fiable. Sur mon système Ubuntu 12.04, vous devez être root pour lire / suivre le lien symbolique. Cela fera échouer L'exemple de Boost et probablement les solutions whereami() publiées.

ce post est très long mais discute des problèmes réels et présente le code qui fonctionne réellement avec la validation par rapport à une suite de test.

La meilleure façon de trouver votre programme est de retracer les étapes de la utilisations du système. Ceci est fait en utilisant argv[0] résolu contre la racine du système de fichiers, pwd, l'environnement de chemin et en considérant les liens symboliques, et la canonalisation de chemin. C'est de mémoire, mais je l'ai fait dans le passé avec succès et testé dans une variété de situations différentes. Il n'est pas garanti, mais si ce n'est pas vous avez probablement beaucoup plus de problèmes et qu'il est plus fiable que n'importe quelles autres méthodes discutées. Il existe des situations sur un système compatible Unix dans quelle manipulation correcte de argv[0] ne vous mènera pas à votre programme, mais alors vous exécutez dans un environnement certifié cassé. Il est également assez portable pour tous les systèmes dérivés D'Unix depuis environ 1970 et même certains systèmes non dérivés D'Unix car il repose essentiellement sur libc() fonctionnalité standard et fonctionnalité de ligne de commande standard. Il devrait fonctionner sur Linux (toutes les versions), Android, Chrome OS, Minix, Unix Bell Labs D'origine, FreeBSD, NetBSD, OpenBSD, BSD X. x, SunOS, Solaris, SYSV, HPUX, Concentrix, SCO, Darwin, AIX, OS X, Nextstep, etc. Et avec une petite modification probablement VMS, VM/CMS, DOS/Windows, ReactOS, OS/2, etc. Si un programme a été lancé directement à partir d'un environnement GUI, il aurait dû mettre argv[0] sur un chemin absolu.

comprendre que presque chaque shell sur chaque système D'exploitation compatible Unix qui a jamais été publié trouve essentiellement les programmes de la même façon et met en place l'environnement d'exploitation presque de la même manière (avec certains accessoires en option). Et tout autre programme qui lance un programme est prévu pour créer le même environnement (argv, chaînes d'environnement, etc.) pour que le programme comme s'il était exécuté à partir d'un shell, avec quelques options supplémentaires. Un programme ou un utilisateur peut configurer un environnement qui s'écarte de cette convention pour d'autres programmes subordonnés qu'il lance, mais s'il le fait, il s'agit d'un bug et le programme n'a aucune attente raisonnable que le programme subordonné ou ses subordonnés fonctionneront correctement.

les valeurs possibles de argv[0] comprennent:

  • /path/to/executable - chemin absolu
  • ../bin/executable - relative à pwd
  • bin/executable - relative à pwd
  • ./foo - relative à pwd
  • executable - basename, trouver dans le chemin
  • bin//executable - relative à pwd, non-canonique
  • src/../bin/executable - par rapport à la dps, non-canonique, retour en arrière
  • bin/./echoargc - relative à pwd, non-canonique

valeurs que vous ne devriez pas voir:

  • ~/bin/executable - réécrit avant l'exécution de votre programme.
  • ~user/bin/executable - réécrit avant l'exécution de votre programme
  • alias - réécrit avant l'exécution de votre programme
  • $shellvariable - réécrit avant l'exécution de votre programme
  • *foo* - Joker, réécrit avant que votre programme ne tourne, pas très utile
  • ?foo? - Joker, réécrit avant que votre programme ne tourne, pas très utile

en outre, ceux-ci peuvent contenir des noms de chemins non canoniques et plusieurs couches de liens symboliques. Dans certains cas, il peut y avoir plusieurs liens durs vers le même programme. Pour exemple, /bin/ls , /bin/ps , /bin/chmod , /bin/rm , etc. peut-être des liens en dur vers /bin/busybox .

pour vous trouver, suivez les étapes ci-dessous:

  • Enregistrer pwd, CHEMIN d'accès, et argv[0] à l'entrée de votre programme (ou de l'initialisation de votre bibliothèque), car ils peuvent changer par la suite.

  • facultatif: en particulier pour les systèmes non-Unix, séparer mais ne pas jeter le chemin nom d'hôte / utilisateur / lecteur préfixe partie, s'il est présent; la partie qui précède souvent deux points ou suit une initiale "//".

  • si argv[0] est un chemin absolu, utilisez-le comme point de départ. Un chemin absolu commence probablement par " / "mais sur certains systèmes non-Unix il peut commencer par" \ " ou une lettre de lecteur ou un préfixe de nom suivi d'un deux-points.

  • Sinon si argv[0] est un chemin relatif (contient "/" ou "\" mais il ne démarre pas avec elle, tels que "../../bin/foo", puis combiner pwd+" / "+argv[0] (utiliser le répertoire de travail actuel à partir de quand le programme a commencé, pas courant).

  • Sinon si argv[0] est un nom de base simple (pas de slashes), puis le combiner avec chaque entrée dans la variable D'environnement PATH tour à tour et essayer ceux-ci et utiliser le premier qui réussit.

  • optionnel: essayer la plate-forme même spécifiques /proc/self/exe , /proc/curproc/file (BSD), et (char *)getauxval(AT_EXECFN) , et dlgetname(...) si présent. Vous pourriez même essayer ces méthodes avant argv[0] , si elles sont disponibles et que vous ne rencontrez pas de problèmes de permission. Dans le cas peu probable (lorsque vous considérez toutes les versions de tous les systèmes) qu'ils sont présents et n'échouent pas, ils pourraient faire plus autorité.

  • optionnel: CONTRÔLE d'un nom de chemin passé en utilisant une ligne de commande paramètre.

  • optionnel: vérifiez si un chemin d'accès dans l'environnement est explicitement passé par votre script d'enrubannage, le cas échéant.

  • en Option: Comme un dernier recours, essayez de variable d'environnement "_". Il pourrait pointer vers un programme entièrement différent, tel que le shell users.

  • résoudre symlinks, il peut y avoir plusieurs couches. Il y a la possibilité de les boucles infinies, mais si elles existent, votre programme ne sera probablement pas appelé.

  • Accepter le nom de fichier par la résolution de sous-chaînes comme "/foo/../bar/" a "/bar/". Notez que cela peut potentiellement changer la signification si vous traversez un point de montage réseau, donc la canonisation n'est pas toujours une bonne chose. Sur un serveur de réseau ".."dans symlink peut être utilisé pour parcourir un chemin vers un autre fichier dans le contexte du serveur plutôt que sur le client. Dans ce cas, vous vous voulez probablement le contexte client pour que la canonisation soit ok. Convertissez aussi des modèles comme "/./" a "/" et "//" a "/". Dans shell, readlink --canonicalize résoudra plusieurs liens symboliques et canonicalisera le nom. Chase peut faire la même chose mais n'est pas installé. realpath() ou canonicalize_file_name() , s'ils sont présents, peuvent être utiles.

si realpath() n'existe pas au moment de la compilation, vous pourriez emprunter une copie d'une distribution de bibliothèque autorisée de façon permissive, et la compiler en vous plutôt que de réinventer la roue. Fixez le dépassement de tampon potentiel (passez dans size of output buffer, pensez à strncpy () vs strcpy ()) si vous utilisez un tampon inférieur à PATH_MAX. Il peut être plus facile d'utiliser une copie privée renommée plutôt que de tester si elle existe. Copie de licence Permissive d'android / darwin/bsd: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c

Soyez conscient que plusieurs tentatives peuvent être réussies ou partiellement réussies et qu'elles peuvent ne pas toutes pointer vers le même exécutable, alors envisagez de vérifier votre exécutable; cependant, vous pouvez ne pas avoir la permission de lecture - si vous ne pouvez pas le lire, ne le traitez pas comme un échec. Ou vérifier quelque chose à proximité de votre exécutable, comme l' "../lib/" répertoire que vous essayez de trouver. Vous pouvez avoir plusieurs versions, empaqueté et compilé localement versions, local et les versions réseau, et local et USB des versions portables, etc. et il y a une petite possibilité que vous pourriez obtenir deux résultats incompatibles de différentes méthodes de localisation. Et "_" peut simplement pointer vers le mauvais programme.

un programme utilisant execve peut délibérément définir argv[0] pour être incompatible avec le chemin réel utilisé pour charger le programme et le chemin corrompu,"_", pwd, etc. bien qu'il n'y ait généralement pas beaucoup de raisons de le faire; mais cela pourrait avoir des implications de sécurité si vous avez vulnérable code qui ignore le fait que votre environnement d'exécution peut être modifié de diverses façons, y compris, mais pas limité, à celui-ci (chroot, système de fichiers fuse, liens durs, etc.) Il est possible pour les commandes shell de définir le chemin mais de ne pas l'exporter.

vous n'avez pas nécessairement besoin de coder pour les systèmes non-Unix mais ce serait une bonne idée d'être conscient de certaines des particularités afin que vous puissiez écrire le code d'une manière telle qu'il n'est pas aussi difficile pour quelqu'un de porter plus tard. Être conscient que certains systèmes (DEC VMS, DOS, URLs, etc.) peut avoir des noms de disques ou d'autres préfixes qui se terminent par un deux-points tel que "C:\", "sys$drive:[foo]bar", et "file:///foo/bar/baz". Les anciens systèmes VMS DEC utilisent "[" et"] " pour enfermer la partie répertoire du chemin bien que cela puisse avoir changé si votre programme est compilé dans un environnement POSIX. Certains systèmes, comme VMS, peuvent avoir une version de fichier (séparée par un point-virgule à la fin). Certains systèmes utilisent deux slashes consécutifs comme dans "//en voiture/chemin/vers/fichier" ou "user@host:/path/to/file" (commande scp) ou "fichier://nom du serveur/chemin/vers/fichier" (URL). Dans certains cas (DOS, windoze), le chemin peut avoir différents caractères de séparateur - ";" vs ":" et "\" vs "/" pour un séparateur de chemin. Dans csh / tsh il y a "path" (délimité par des espaces) et "PATH" délimité par des points, mais votre programme devrait recevoir PATH pour que vous n'ayez pas à vous soucier de path. Le DOS et d'autres systèmes peuvent avoir des chemins relatifs qui commencent par un préfixe d'entraînement. C: foo.EXE se réfère à toto.exe dans le répertoire courant sur le lecteur C, vous devez donc rechercher le répertoire courant sur C: et l'utiliser pour pwd.

exemple de liens symboliques et d'enveloppes sur mon système:

/usr/bin/google-chrome is symlink to
/etc/alternatives/google-chrome  which is symlink to
/usr/bin/google-chrome-stable which is symlink to
/opt/google/chrome/google-chrome which is a bash script which runs
/opt/google/chome/chrome

notez que l'utilisateur facture posté un lien ci-dessus vers un programme chez HP qui gère les trois cas de base de argv[0] . Il a besoin de quelques changements, cependant:

  • Il sera nécessaire de réécrire tous les strcat() et strcpy() utiliser strncat() et strncpy() . Même si les variables sont déclarées de longueur PATHMAX, une valeur d'entrée de longueur PATHMAX-1 plus La longueur des chaînes concaténées est > PATHMAX et une valeur d'entrée de longueur PATHMAX serait untermined.
  • il doit être réécrit comme une fonction de bibliothèque, plutôt que juste pour imprimer des résultats.
    • il ne parvient pas à canoniser noms (utiliser la fonction realpath code que j'ai lié ci-dessus)
    • il ne résout pas les liens symboliques (utilisez le code realpath)

donc, si vous combinez à la fois le code HP et le code realpath et que vous fixez les deux pour être résistant aux débordements de tampon, alors vous devriez avoir quelque chose qui peut correctement interpréter argv[0] .

ce qui suit illustre les valeurs réelles de argv[0] pour diverses façons de invoquant le même programme sur Ubuntu 12.04. Et oui, le programme a été accidentellement nommé echoargc au lieu d'echoargv. Ceci a été fait en utilisant un script pour nettoyer la copie, mais le faire manuellement dans shell donne les mêmes résultats (sauf que les alias ne fonctionnent pas dans script à moins que vous ne les activiez explicitement).

cat ~/src/echoargc.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
main(int argc, char **argv)
{
  printf("  argv[0]=\"%s\"\n", argv[0]);
  sleep(1);  /* in case run from desktop */
}
tcc -o ~/bin/echoargc ~/src/echoargc.c 
cd ~
/home/whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
echoargc
  argv[0]="echoargc"
bin/echoargc
  argv[0]="bin/echoargc"
bin//echoargc
  argv[0]="bin//echoargc"
bin/./echoargc
  argv[0]="bin/./echoargc"
src/../bin/echoargc
  argv[0]="src/../bin/echoargc"
cd ~/bin
*echo*
  argv[0]="echoargc"
e?hoargc
  argv[0]="echoargc"
./echoargc
  argv[0]="./echoargc"
cd ~/src
../bin/echoargc
  argv[0]="../bin/echoargc"
cd ~/junk
~/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
~whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
alias echoit=~/bin/echoargc
echoit
  argv[0]="/home/whitis/bin/echoargc"
echoarg=~/bin/echoargc
$echoarg
  argv[0]="/home/whitis/bin/echoargc"
ln -s ~/bin/echoargc junk1
./junk1
  argv[0]="./junk1"
ln -s /home/whitis/bin/echoargc junk2
./junk2
  argv[0]="./junk2"
ln -s junk1 junk3
./junk3
  argv[0]="./junk3"


gnome-desktop-item-edit --create-new ~/Desktop
# interactive, create desktop link, then click on it
  argv[0]="/home/whitis/bin/echoargc"
# interactive, right click on gnome application menu, pick edit menus
# add menu item for echoargc, then run it from gnome menu
 argv[0]="/home/whitis/bin/echoargc"

 cat ./testargcscript 2>&1 | sed -e 's/^/    /g'
#!/bin/bash
# echoargc is in ~/bin/echoargc
# bin is in path
shopt -s expand_aliases
set -v
cat ~/src/echoargc.c
tcc -o ~/bin/echoargc ~/src/echoargc.c 
cd ~
/home/whitis/bin/echoargc
echoargc
bin/echoargc
bin//echoargc
bin/./echoargc
src/../bin/echoargc
cd ~/bin
*echo*
e?hoargc
./echoargc
cd ~/src
../bin/echoargc
cd ~/junk
~/bin/echoargc
~whitis/bin/echoargc
alias echoit=~/bin/echoargc
echoit
echoarg=~/bin/echoargc
$echoarg
ln -s ~/bin/echoargc junk1
./junk1
ln -s /home/whitis/bin/echoargc junk2
./junk2
ln -s junk1 junk3
./junk3

ces exemples illustrent que les techniques décrites dans ce post devraient fonctionner dans un large éventail de circonstances et pourquoi certaines des étapes sont nécessaires.

EDIT: maintenant, le programme qui imprime argv[0] a été mis à jour pour réellement se trouver.

// Copyright 2015 by Mark Whitis.  License=MIT style
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

// "look deep into yourself, Clarice"  -- Hanibal Lector
char findyourself_save_pwd[PATH_MAX];
char findyourself_save_argv0[PATH_MAX];
char findyourself_save_path[PATH_MAX];
char findyourself_path_separator='/';
char findyourself_path_separator_as_string[2]="/";
char findyourself_path_list_separator[8]=":";  // could be ":; "
char findyourself_debug=0;

int findyourself_initialized=0;

void findyourself_init(char *argv0)
{

  getcwd(findyourself_save_pwd, sizeof(findyourself_save_pwd));

  strncpy(findyourself_save_argv0, argv0, sizeof(findyourself_save_argv0));
  findyourself_save_argv0[sizeof(findyourself_save_argv0)-1]=0;

  strncpy(findyourself_save_path, getenv("PATH"), sizeof(findyourself_save_path));
  findyourself_save_path[sizeof(findyourself_save_path)-1]=0;
  findyourself_initialized=1;
}


int find_yourself(char *result, size_t size_of_result)
{
  char newpath[PATH_MAX+256];
  char newpath2[PATH_MAX+256];

  assert(findyourself_initialized);
  result[0]=0;

  if(findyourself_save_argv0[0]==findyourself_path_separator) {
    if(findyourself_debug) printf("  absolute path\n");
     realpath(findyourself_save_argv0, newpath);
     if(findyourself_debug) printf("  newpath=\"%s\"\n", newpath);
     if(!access(newpath, F_OK)) {
        strncpy(result, newpath, size_of_result);
        result[size_of_result-1]=0;
        return(0);
     } else {
    perror("access failed 1");
      }
  } else if( strchr(findyourself_save_argv0, findyourself_path_separator )) {
    if(findyourself_debug) printf("  relative path to pwd\n");
    strncpy(newpath2, findyourself_save_pwd, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    strncat(newpath2, findyourself_save_argv0, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    realpath(newpath2, newpath);
    if(findyourself_debug) printf("  newpath=\"%s\"\n", newpath);
    if(!access(newpath, F_OK)) {
        strncpy(result, newpath, size_of_result);
        result[size_of_result-1]=0;
        return(0);
     } else {
    perror("access failed 2");
      }
  } else {
    if(findyourself_debug) printf("  searching $PATH\n");
    char *saveptr;
    char *pathitem;
    for(pathitem=strtok_r(findyourself_save_path, findyourself_path_list_separator,  &saveptr); pathitem; pathitem=strtok_r(NULL, findyourself_path_list_separator, &saveptr) ) {
       if(findyourself_debug>=2) printf("pathitem=\"%s\"\n", pathitem);
       strncpy(newpath2, pathitem, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       strncat(newpath2, findyourself_save_argv0, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       realpath(newpath2, newpath);
       if(findyourself_debug) printf("  newpath=\"%s\"\n", newpath);
      if(!access(newpath, F_OK)) {
          strncpy(result, newpath, size_of_result);
          result[size_of_result-1]=0;
          return(0);
      } 
    } // end for
    perror("access failed 3");

  } // end else
  // if we get here, we have tried all three methods on argv[0] and still haven't succeeded.   Include fallback methods here.
  return(1);
}

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

  char newpath[PATH_MAX];
  printf("  argv[0]=\"%s\"\n", argv[0]);
  realpath(argv[0], newpath);
  if(strcmp(argv[0],newpath)) { printf("  realpath=\"%s\"\n", newpath); }
  find_yourself(newpath, sizeof(newpath));
  if(1 || strcmp(argv[0],newpath)) { printf("  findyourself=\"%s\"\n", newpath); }
  sleep(1);  /* in case run from desktop */
}

et voici la sortie qui démontre que dans chacun des tests précédents il s'est effectivement trouvé.

tcc -o ~/bin/echoargc ~/src/echoargc.c 
cd ~
/home/whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
echoargc
  argv[0]="echoargc"
  realpath="/home/whitis/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin/echoargc
  argv[0]="bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin//echoargc
  argv[0]="bin//echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin/./echoargc
  argv[0]="bin/./echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
src/../bin/echoargc
  argv[0]="src/../bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/bin
*echo*
  argv[0]="echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
e?hoargc
  argv[0]="echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
./echoargc
  argv[0]="./echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/src
../bin/echoargc
  argv[0]="../bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/junk
~/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
~whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
alias echoit=~/bin/echoargc
echoit
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
echoarg=~/bin/echoargc
$echoarg
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
rm junk1 junk2 junk3
ln -s ~/bin/echoargc junk1
./junk1
  argv[0]="./junk1"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
ln -s /home/whitis/bin/echoargc junk2
./junk2
  argv[0]="./junk2"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
ln -s junk1 junk3
./junk3
  argv[0]="./junk3"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"

les deux lancements GUI décrits ci-dessus trouvent aussi correctement le programme.

il y a un piège potentiel. La fonction access() supprime les permissions si le programme setuid avant le test. S'il y a une situation où le programme peut être trouvé en tant qu'utilisateur élevé mais pas en tant qu'Utilisateur régulier, alors il pourrait y avoir une situation où ces tests échoueraient, bien qu'il soit peu probable que le programme puisse réellement être exécuté dans ces circonstances. On pourrait utiliser euidaccess() à la place. Il est possible, cependant, qu'il puisse trouver un programme inacceptable plus tôt sur le chemin que l'utilisateur réel pourrait.

16
répondu whitis 2017-05-23 10:31:31

consultez la bibliothèque whatami de Gregory Pakosz (qui n'a qu'un seul fichier C); il vous permet d'obtenir le chemin complet de l'exécutable actuel sur une variété de plateformes. Actuellement, il est disponible en tant que repo sur github ici .

10
répondu prideout 2015-09-27 00:26:04

une alternative sous Linux à l'utilisation de /proc/self/exe ou argv[0] est d'utiliser les informations transmises par l'interpréteur ELF, mis à disposition par glibc en tant que tel:

#include <stdio.h>
#include <sys/auxv.h>

int main(int argc, char **argv)
{
    printf("%s\n", (char *)getauxval(AT_EXECFN));
    return(0);
}

notez que getauxval est une extension glibc, et pour être robuste, vous devez vérifier qu'elle ne retourne pas NULL (indiquant que L'interpréteur ELF n'a pas fourni le paramètre AT_EXECFN ), mais je ne pense pas que ce soit jamais un problème sur Linux.

4
répondu Dolda2000 2014-05-13 00:52:20

si vous avez jamais eu à soutenir, dire, Mac OS X, qui n'a pas /proc/, quoi auriez-vous fait? Utilisez # ifdefs pour isoler le code propre à la plate-forme (NSBundle, par exemple)?

Oui isoler le code spécifique de la plate-forme avec #ifdefs est la méthode conventionnelle.

une autre approche serait d'avoir un en-tête propre #ifdef - moins qui contient des déclarations de fonction et met implémentations dans des fichiers sources spécifiques à la plate-forme. Par exemple, regardez comment la bibliothèque Poco C++ fait quelque chose de similaire pour sa classe Environment .

3
répondu StackedCrooked 2015-05-20 06:51:20

pour que cela fonctionne de manière fiable sur toutes les plateformes, il faut utiliser les instructions #ifdef.

le code ci-dessous trouve le chemin de l'exécutable sous Windows, Linux, MacOS, Solaris ou FreeBSD (bien que FreeBSD ne soit pas testé). Il utilise boost > = 1,55.0 pour simplifier le code mais il est assez facile à enlever si vous voulez. Il suffit d'utiliser les définitions comme _MSC_VER et __linux comme le système D'exploitation et le compilateur l'exigent.

#include <string>
#include <boost/predef/os.h>

#if (BOOST_OS_WINDOWS)
#  include <stdlib.h>
#elif (BOOST_OS_SOLARIS)
#  include <stdlib.h>
#  include <limits.h>
#elif (BOOST_OS_LINUX)
#  include <unistd.h>
#  include <limits.h>
#elif (BOOST_OS_MACOS)
#  include <mach-o/dyld.h>
#elif (BOOST_OS_BSD_FREE)
#  include <sys/types.h>
#  include <sys/sysctl.h>
#endif

/*
 * Returns the full path to the currently running executable,
 * or an empty string in case of failure.
 */
std::string getExecutablePath() {
#if (BOOST_OS_WINDOWS)
    char *exePath;
    if (_get_pgmptr(&exePath) != 0)
        exePath = "";
#elif (BOOST_OS_SOLARIS)
    char exePath[PATH_MAX];
    if (realpath(getexecname(), exePath) == NULL)
        exePath[0] = '"151900920"';
#elif (BOOST_OS_LINUX)
    char exePath[PATH_MAX];
    ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath));
    if (len == -1 || len == sizeof(exePath))
        len = 0;
    exePath[len] = '"151900920"';
#elif (BOOST_OS_MACOS)
    char exePath[PATH_MAX];
    uint32_t len = sizeof(exePath);
    if (_NSGetExecutablePath(exePath, &len) != 0) {
        exePath[0] = '"151900920"'; // buffer too small (!)
    } else {
        // resolve symlinks, ., .. if possible
        char *canonicalPath = realpath(exePath, NULL);
        if (canonicalPath != NULL) {
            strncpy(exePath,canonicalPath,len);
            free(canonicalPath);
        }
    }
#elif (BOOST_OS_BSD_FREE)
    char exePath[2048];
    int mib[4];  mib[0] = CTL_KERN;  mib[1] = KERN_PROC;  mib[2] = KERN_PROC_PATHNAME;  mib[3] = -1;
    size_t len = sizeof(exePath);
    if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0)
        exePath[0] = '"151900920"';
#endif
    return std::string(exePath);
}

la version ci-dessus retourne full chemins incluant le nom de l'exécutable. Si à la place vous voulez le chemin sans le nom exécutable, #include boost/filesystem.hpp> et changez l'instruction de retour en:

return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();
3
répondu jtbr 2015-10-21 14:16:41

vous pouvez utiliser argv[0] et analyser la variable D'environnement PATH. Regardez: un échantillon d'un programme qui peut se trouver

2
répondu bill 2009-06-21 07:30:51

selon la version de QNX Neutrino , il y a différentes façons de trouver le chemin complet et le nom du fichier exécutable qui a été utilisé pour démarrer le processus d'exécution. L'Identificateur de processus est <PID> . Essayez ce qui suit:

  1. Si le fichier /proc/self/exefile existe, son contenu est l'information demandée.
  2. si le fichier /proc/<PID>/exefile existe, alors son contenu est le les informations demandées.
  3. Si le fichier /proc/self/as existe, alors:
    1. open() le fichier.
    2. attribuer un tampon d'au moins sizeof(procfs_debuginfo) + _POSIX_PATH_MAX .
    3. donne ce tampon comme entrée à devctl(fd, DCMD_PROC_MAPDEBUG_BASE,... .
    4. lance le tampon sur un procfs_debuginfo* .
    5. les renseignements demandés se trouvent dans le champ path de la structure procfs_debuginfo . avertissement : pour une raison quelconque, parfois, QNX omet la première barre oblique / du chemin du fichier. Préparez ce / au besoin.
    6. nettoyer (fermer le fichier, libérer le tampon, etc.).
  4. Essayez la procédure décrite dans 3. avec le fichier /proc/<PID>/as .
  5. Essayer dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)dlinfo est un Dl_info structure dont la dli_fname peut contenir la demande information.

j'espère que cette aide.

1
répondu Koutheir Attouchi 2015-11-13 15:52:44

AFAIK, pas question. Et il y a aussi une ambuiguité: que voudriez-vous obtenir comme réponse si le même exécutable a plusieurs liens durs "pointant" vers lui? (Les liens durs ne "pointent" pas réellement, ils sont le même fichier, juste à un autre endroit dans la hiérarchie FS.) Une fois execve () exécute avec succès un nouveau binaire, toutes les informations sur ses arguments sont perdues.

0
répondu zvrba 2009-06-21 07:14:44

méthode plus portable pour obtenir le chemin d'accès nom de l'image exécutable:

ps peut vous donner le chemin de l'exécutable, étant donné que vous avez l'id de processus. Aussi ps est un utilitaire POSIX donc il devrait être portable

donc si l'identifiant du processus est 249297 alors cette commande ne vous donne que le nom du chemin.

    ps -p 24297 -o comm --no-heading

explication des arguments

-p - sélectionne processus donné

- o comm - affiche le nom de la commande (- O cmd sélectionne la ligne de commande entière)

--no-cap - ne pas afficher une ligne d'en-tête, juste à la sortie.

un programme C peut exécuter ceci via popen.

0
répondu MichaelMoser 2014-03-26 15:05:18

si vous utilisez C, vous pouvez utiliser la fonction getwd:

int main()
{       
 char buf[4096];
 getwd(buf);
 printf(buf);
}

vous imprimerez sur la sortie standard, le répertoire courant de l'exécutable.

-2
répondu Lucas Ruelle 2016-08-15 09:42:50

le chemin de valeur absolue d'un programme est dans le PWD de l'envp de votre fonction principale, il y a aussi une fonction en C appelée getenv, donc il y a ça.

-9
répondu dacres 2012-01-12 14:37:11