Encontrando o caminho do executável atual sem / proc / self / exe

Parece-me que o Linux é fácil com / proc / self / exe. Mas gostaria de saber se existe uma maneira conveniente de encontrar o diretório do aplicativo atual em C / C ++ com interfaces entre plataformas. Eu vi alguns projetos mexendo com argv [0], mas isso não parece totalmente confiável.

Se você já teve que suportar, digamos, o Mac OS X, que não tem / proc /, o que você teria feito? Use #ifdefs para isolar o código específico da plataforma (NSBundle, por exemplo)? Ou tente deduzir o caminho do executável de argv [0], $ PATH e outros, arriscando encontrar bugs nos casos de borda?

Algumas interfaces específicas do sistema operacional:

  • Mac OS X: _NSGetExecutablePath() ( homem 3 dyld )
  • Linux: readlink /proc/self/exe
  • Solaris: getexecname()
  • FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
  • FreeBSD se tiver procfs: readlink /proc/curproc/file (o FreeBSD não tem procfs por padrão)
  • NetBSD: readlink /proc/curproc/exe
  • DragonFly BSD: readlink /proc/curproc/file
  • Windows: GetModuleFileName() com hModule = NULL

O método portátil (mas menos confiável) é usar argv[0] . Embora possa ser definido como qualquer coisa pelo programa de chamada, por convenção é definido como um nome de caminho do executável ou um nome que foi encontrado usando $PATH .

Algumas shells, incluindo bash e ksh, definem a variável de ambiente ” _ ” para o caminho completo do executável antes de ser executado. Nesse caso, você pode usar getenv("_") para obtê-lo. No entanto, isso não é confiável porque nem todos os shells fazem isso, e pode ser definido como qualquer coisa ou ser deixado de um processo pai que não o alterou antes de executar o programa.

O uso de /proc/self/exe é não portátil e não confiável. No meu sistema Ubuntu 12.04, você deve ser root para ler / seguir o link simbólico. Isso fará com que o exemplo de Boost e provavelmente as soluções whereami() postadas falhem.

Essa postagem é muito longa, mas discute os problemas reais e apresenta o código que realmente funciona junto com a validação em relação a uma suíte de testes.

A melhor maneira de encontrar o seu programa é refazer os mesmos passos que o sistema usa. Isso é feito usando argv[0] resolvido contra raiz do sistema de arquivos, pwd, ambiente de caminho e considerando links simbólicos e canonização do nome do caminho. Isso é da memory, mas eu fiz isso com sucesso no passado e testei-o em várias situações diferentes. Não é garantido que funcione, mas se não o fizer, provavelmente terá problemas muito maiores e é mais confiável em geral do que qualquer um dos outros methods discutidos. Existem situações em um sistema compatível com Unix no qual o tratamento adequado de argv[0] não irá levá-lo ao seu programa, mas você estará executando em um ambiente que pode ser quebrado. Ele também é razoavelmente portátil para todos os sistemas derivados do Unix desde por volta de 1970 e até mesmo alguns sistemas não derivados do Unix, já que ele depende basicamente da funcionalidade padrão libc () e da funcionalidade de linha de comando padrão. Deve funcionar em Linux (todas as versões), Android, SO Chrome, Minix, Unix original, FreeBSD, NetBSD, OpenBSD, BSD xx, SunOS, Solaris, SYSV, HPX, Concentrix, SCO, Darwin, AIX, OS X, Nextstep, etc. E com uma pequena modificação provavelmente VMS, VM / CMS, DOS / Windows, ReactOS, OS / 2, etc. Se um programa foi lançado diretamente de um ambiente GUI, ele deve ter definido argv[0] para um valor absoluto. caminho.

Entenda que quase todos os shell em todos os sistemas operacionais compatíveis com Unix que já foram lançados basicamente encontram os programas da mesma maneira e configuram o ambiente operacional quase da mesma maneira (com alguns extras opcionais). E qualquer outro programa que inicie um programa deverá criar o mesmo ambiente (argv, strings de ambiente, etc.) para esse programa como se fosse executado a partir de um shell, com alguns extras opcionais. Um programa ou usuário pode configurar um ambiente que se desvia dessa convenção para outros programas subordinados que ele lança, mas se isso ocorrer, isso é um bug e o programa não tem expectativa razoável de que o programa subordinado ou seus subordinados funcionem corretamente.

Valores possíveis de argv[0] incluem:

  • /path/to/executable – caminho absoluto
  • ../bin/executable – relativo ao pwd
  • bin/executable – relativo ao pwd
  • ./foo – relativo ao pwd
  • executable – basename, encontrar no caminho
  • bin//executable – relativo ao pwd, não canônico
  • src/../bin/executable – relativo a pwd, não-canônico, backtracking
  • bin/./echoargc – relativo a pwd, não-canônico

Valores que você não deve ver:

  • ~/bin/executable – reescrito antes do seu programa ser executado.
  • ~user/bin/executable – reescrito antes do seu programa ser executado
  • alias – reescrito antes do seu programa ser executado
  • $shellvariable – reescrito antes do seu programa ser executado
  • *foo* – curinga, reescrito antes do seu programa ser executado, não é muito útil
  • ?foo? – curinga, reescrito antes do seu programa ser executado, não é muito útil

Além disso, eles podem conter nomes de caminho não canônicos e várias camadas de links simbólicos. Em alguns casos, pode haver vários links físicos para o mesmo programa. Por exemplo, /bin/ls , /bin/ps , /bin/chmod , /bin/rm , etc. podem ser hard links para /bin/busybox .

Para se encontrar, siga os passos abaixo:

  • Salve pwd, PATH e argv [0] na input do seu programa (ou boot de sua biblioteca), pois eles podem ser alterados mais tarde.

  • Opcional: particularmente para sistemas não-Unix, separe, mas não descarte a parte do prefixo do caminho / usuário / unidade de caminho, se presente; a parte que frequentemente precede dois pontos ou segue um “//” inicial.

  • Se argv[0] é um caminho absoluto, use isso como um ponto de partida. Um caminho absoluto provavelmente começa com “/”, mas em alguns sistemas não-Unix ele pode começar com “\” ou uma letra de unidade ou prefixo de nome seguido por dois-pontos.

  • Se argv[0] for um caminho relativo (contém “/” ou “\”, mas não inicia com ele, como “../../bin/foo”, então combine pwd + “/” + argv [ 0] (use o diretório de trabalho atual de quando o programa foi iniciado, não atual).

  • Senão, se argv [0] for um nome de base simples (sem barras), combine-o com cada input na variável de ambiente PATH, por sua vez, e tente-as e use a primeira que tiver êxito.

  • Opcional: Senão tente a plataforma específica /proc/self/exe , /proc/curproc/file (BSD) e (char *)getauxval(AT_EXECFN) e dlgetname(...) se presente. Você pode até tentar estes antes dos methods baseados em argv[0] , se eles estiverem disponíveis e você não encontrar problemas de permissão. No evento improvável (quando você considera todas as versões de todos os sistemas) que elas estão presentes e não falham, elas podem ser mais autoritativas.

  • Opcional: verifique um nome de caminho passado usando um parâmetro de linha de comando.

  • Opcional: verifique um nome de caminho no ambiente explicitamente passado pelo seu script de wrapper, se houver.

  • Opcional: Como último recurso, tente a variável de ambiente “_”. Pode apontar para um programa diferente, como o shell do usuário.

  • Resolver links simbólicos, pode haver várias camadas. Existe a possibilidade de loops infinitos, embora, se existirem, seu programa provavelmente não será invocado.

  • Canonicalize nome do arquivo, resolvendo substrings como “/foo/../bar/” para “/ bar /”. Observe que isso pode mudar o significado se você cruzar um ponto de assembly de rede, portanto, a canonização nem sempre é uma coisa boa. Em um servidor de rede, “..” no symlink pode ser usado para percorrer um caminho para outro arquivo no contexto do servidor em vez de no cliente. Neste caso, você provavelmente quer o contexto do cliente para que a canonização esteja correta. Converta também padrões como “/./” para “/” e “//” para “/”. No shell, readlink --canonicalize resolverá vários links simbólicos e canoniza o nome. Chase pode fazer semelhante, mas não está instalado. realpath() ou canonicalize_file_name() , se presente, pode ajudar.

Se realpath() não existir em tempo de compilation, você pode pegar uma cópia emprestada de uma distribuição de biblioteca permissivamente licenciada e compilá-la em si mesmo, em vez de reinventar a roda. Corrija o possível estouro de buffer (passe o tamanho do buffer de saída, pense em strncpy () vs strcpy ()) se você estiver usando um buffer menor que PATH_MAX. Pode ser mais fácil apenas usar uma cópia privada renomeada em vez de testar se ela existe. Cópia de licença permissiva do android / darwin / bsd: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c

Esteja ciente de que várias tentativas podem ser bem-sucedidas ou parcialmente bem-sucedidas e nem todas apontam para o mesmo executável, portanto, considere verificar seu executável; no entanto, você pode não ter permissão de leitura – se você não puder lê-lo, não o considere como uma falha. Ou verifique algo próximo ao seu executável, como o diretório “../lib/” que você está tentando encontrar. Você pode ter várias versões, versões empacotadas e compiladas localmente, versões locais e de rede, e versões portáteis locais e com unidade USB, etc., e há uma pequena possibilidade de que você obtenha dois resultados incompatíveis de diferentes methods de localização. E “_” pode simplesmente apontar para o programa errado.

Um programa usando execve pode deliberadamente definir argv[0] para ser incompatível com o caminho real usado para carregar o programa e corromper o PATH, “_”, pwd, etc., embora geralmente não haja muitas razões para isso; mas isso pode ter implicações de segurança se você tiver código vulnerável que ignora o fato de que seu ambiente de execução pode ser alterado de várias maneiras, incluindo, mas não limitado, a este (sistema de arquivos chroot, fusível, links físicos, etc.) para comandos shell para definir o PATH, mas não exportá-lo.

Você não precisa necessariamente codificar para sistemas não-Unix, mas seria uma boa ideia estar ciente de algumas das peculiaridades para que você possa escrever o código de tal forma que não seja tão difícil para alguém portar mais tarde . Esteja ciente de que alguns sistemas (DEC VMS, DOS, URLs, etc.) podem ter nomes de unidade ou outros prefixos que terminam com dois pontos, como “C: \”, “sys $ drive: [foo] bar” e “file : /// foo / bar / baz “. Sistemas VMS antigos do DEC usam “[” e “]” para include a parte do diretório do caminho, embora isso possa ter mudado se o programa for compilado em um ambiente POSIX. Alguns sistemas, como o VMS, podem ter uma versão de arquivo (separados por um ponto-e-vírgula no final). Alguns sistemas usam duas barras consecutivas como em “// drive / path / to / file” ou “user @ host: / caminho / para / file” (comando scp) ou “file: // hostname / path / to / file” (URL). Em alguns casos (DOS, windoze), o PATH pode ter diferentes caracteres de separação – “;” vs “:” e “\” vs “/” para um separador de caminho. Em csh / tsh existe “path” (delimitado por espaços) e “PATH” delimitado por dois pontos mas o seu programa deve receber o PATH para que você não precise se preocupar com o caminho. O DOS e alguns outros sistemas podem ter caminhos relativos que começam com um prefixo de unidade. C: foo.exe refere-se ao foo.exe no diretório atual na unidade C, portanto, é necessário procurar o diretório atual em C: e usá-lo para o pwd.

Um exemplo de links simbólicos e wrappers no meu sistema:

 /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 

Observe que a conta do usuário postou um link acima para um programa na HP que lida com os três casos básicos de argv[0] . Ele precisa de algumas mudanças, no entanto:

  • Será necessário rewrite todos os strcat() e strcpy() para usar strncat() e strncpy() . Embora as variables ​​sejam declaradas de comprimento PATHMAX, um valor de input de comprimento PATHMAX-1 mais o comprimento de cadeias concatenadas é> PATHMAX e um valor de input de comprimento PATHMAX não seria terminado.
  • Ele precisa ser reescrito como uma function de biblioteca, em vez de apenas imprimir os resultados.
    • Não consegue canonizar nomes (use o código do caminho real que eu criei link acima)
    • Falha ao resolver links simbólicos (use o código realpath)

Então, se você combinar tanto o código HP quanto o código realpath e consertar ambos para serem resistentes a estouro de buffer, então você deve ter algo que possa interpretar corretamente o argv[0] .

A argv[0] seguir ilustra os valores reais de argv[0] para várias formas de invocar o mesmo programa no Ubuntu 12.04. E sim, o programa foi acidentalmente chamado de echoargc em vez de echoargv. Isso foi feito usando um script para cópia limpa, mas fazê-lo manualmente no shell obtém os mesmos resultados (exceto que os aliases não funcionam no script, a menos que você os habilite explicitamente).

 cat ~/src/echoargc.c #include  #include  #include  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 

Esses exemplos ilustram que as técnicas descritas neste post devem funcionar em uma ampla variedade de circunstâncias e por que algumas das etapas são necessárias.

EDIT: agora, o programa que imprime argv [0] foi atualizado para realmente encontrar-se.

 // Copyright 2015 by Mark Whitis. License=MIT style #include  #include  #include  #include  #include  #include  #include  // "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 */ } 

E aqui está a saída que demonstra que em cada um dos testes anteriores ele realmente se encontrou.

 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" 

Os dois lançamentos da GUI descritos acima também localizam corretamente o programa.

Existe uma armadilha potencial. A function access() remove permissions se o programa for setuid antes do teste. Se houver uma situação em que o programa possa ser encontrado como um usuário elevado, mas não como um usuário comum, pode haver uma situação em que esses testes falharão, embora seja improvável que o programa possa realmente ser executado nessas circunstâncias. Pode-se usar euidaccess () em vez disso. É possível, no entanto, que ele possa encontrar um programa inacessível anteriormente no caminho do que o usuário real poderia.

Confira a biblioteca whereami de Gregory Pakosz (que tem apenas um único arquivo C); Ele permite que você obtenha o caminho completo para o executável atual em uma variedade de plataformas. Atualmente, está disponível como repo no github aqui .

Uma alternativa no Linux para usar /proc/self/exe ou argv[0] é usar a informação passada pelo interpretador ELF, disponibilizada pela glibc como tal:

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

Note que getauxval é uma extensão glibc, e para ser robusto você deve checar para que ele não retorne NULL (indicando que o interpretador ELF não forneceu o parâmetro AT_EXECFN ), mas eu não acho que isso seja realmente um problema no Linux.

Se você já teve que suportar, digamos, o Mac OS X, que não tem / proc /, o que você teria feito? Use #ifdefs para isolar o código específico da plataforma (NSBundle, por exemplo)?

Sim, isolar o código específico da plataforma com #ifdefs é a maneira convencional como isso é feito.

Outra abordagem seria ter um header #ifdef -less limpo contendo declarações de function e colocar as implementações em arquivos de origem específicos da plataforma. Por exemplo, confira como a biblioteca Poco C ++ faz algo semelhante para sua class Environment .

Fazer isso funcionar de maneira confiável em todas as plataformas requer o uso de instruções #ifdef.

O código abaixo encontra o caminho do executável no Windows, Linux, MacOS, Solaris ou FreeBSD (embora o FreeBSD não tenha sido testado). Ele usa boost > = 1.55.0 para simplificar o código, mas é fácil de remover se você quiser. Apenas use define como _MSC_VER e __linux como o SO e o compilador requerem.

 #include  #include  #if (BOOST_OS_WINDOWS) # include  #elif (BOOST_OS_SOLARIS) # include  # include  #elif (BOOST_OS_LINUX) # include  # include  #elif (BOOST_OS_MACOS) # include  #elif (BOOST_OS_BSD_FREE) # include  # include  #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] = '\0'; #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] = '\0'; #elif (BOOST_OS_MACOS) char exePath[PATH_MAX]; uint32_t len = sizeof(exePath); if (_NSGetExecutablePath(exePath, &len) != 0) { exePath[0] = '\0'; // 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] = '\0'; #endif return std::string(exePath); } 

A versão acima retorna caminhos completos, incluindo o nome do executável. Se, em vez disso, você quiser o caminho sem o nome do executável, #include boost/filesystem.hpp> e altere a instrução de retorno para:

 return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string(); 

Você pode usar argv [0] e analisar a variável de ambiente PATH. Olhe para: Uma amostra de um programa que pode encontrar-se

Dependendo da versão do QNX Neutrino , existem diferentes maneiras de localizar o caminho completo e o nome do arquivo executável usado para iniciar o processo em execução. Eu denoto o identificador do processo como . Tente o seguinte:

  1. Se o arquivo /proc/self/exefile existir, seu conteúdo será a informação solicitada.
  2. Se o arquivo /proc//exefile existir, seu conteúdo será a informação solicitada.
  3. Se o arquivo /proc/self/as existir, então:
    1. open() o arquivo.
    2. Alocar um buffer de, pelo menos, sizeof(procfs_debuginfo) + _POSIX_PATH_MAX .
    3. Dê esse buffer como input para devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
    4. Transmita o buffer para um procfs_debuginfo* .
    5. As informações solicitadas estão no campo de path da estrutura procfs_debuginfo . Aviso : Por algum motivo, às vezes, o QNX omite a primeira barra / do caminho do arquivo. Prefira isso / quando necessário.
    6. Limpar (feche o arquivo, libere o buffer, etc.).
  4. Tente o procedimento em 3. com o arquivo /proc//as .
  5. Tente dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo) onde dlinfo é uma estrutura dli_fname cujo dli_fname pode conter a informação solicitada.

Eu espero que isso ajude.

AFAIK, de jeito nenhum. E há também uma ambiguidade: o que você gostaria de obter como resposta se o mesmo executável tiver vários links físicos “apontando” para ele? (Hard-links na verdade não “apontam”, eles são o mesmo arquivo, apenas em outro lugar na hierarquia FS.) Uma vez que execve () executa com sucesso um novo binário, todas as informações sobre seus argumentos são perdidas.

Maneira mais portátil para obter o nome do caminho da imagem executável:

ps pode lhe dar o caminho do executável, dado que você tem o id do processo. Também ps é um utilitário POSIX, então deve ser portátil

Portanto, se o id do processo for 249297, esse comando fornecerá apenas o nome do caminho.

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

Explicação de argumentos

-p – seleciona determinado processo

-o comm – exibe o nome do comando (-o cmd seleciona toda a linha de comando)

–no-heading – não exibe uma linha de título, apenas a saída.

Programa AC pode executar isso via popen.

Se você usa C, você pode usar a function getwd:

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

Isto irá imprimir na saída padrão, o diretório atual do executável.

O caminho de valor absoluto de um programa está no PWD do envp de sua function principal, também existe uma function em C chamada getenv, então existe isso.