Como obtenho o diretório em que um programa está sendo executado?

269

Existe um método independente de plataforma e independente de sistema de arquivos para obter o caminho completo do diretório de onde um programa está sendo executado usando C / C ++? Não deve ser confundido com o diretório de trabalho atual. (Por favor, não sugira bibliotecas, a menos que sejam padrão, como clib ou STL.)

(Se não houver um método independente de plataforma / sistema de arquivos, sugestões que funcionem no Windows e Linux para sistemas de arquivos específicos também são bem-vindas.)

Ashwin Nanjappa
fonte
@chakrit: Isso seria ótimo. (Embora essa questão não costuma chegar no Windows.)
Ashwin Nanjappa
2
A menos que você possa extrair o caminho com segurança argv[0], a técnica dependerá muito do sistema operacional.
David R Tribble
1
Apenas para esclarecer: o 'diretório atual' ou 'o diretório do qual o programa está sendo executado' (na terminologia da pergunta) é o diretório em que o arquivo de imagem do programa (arquivo ~ .exe) está localizado e o ' diretório de trabalho atual ' é o diretório que é preenchido automaticamente se o programa usa caminhos relativos?
colemik
3
Quando você #include <windows.h>, o Windows coloca automaticamente um char*no caminho do executável _pgmptr. Você não precisa chamar funções extras ou assumir que não há lixo se estiver trabalhando apenas no Windows.
Rsethc
1
Embora o comentário seja de três anos atrás, eu gostaria de expandir o comentário de rsethc _pgmptr. A documentação do MSDN afirma que as variáveis _pgmptre _wpgmptrestão obsoletas e você deve usar a função _get_pgmptr(char**)ou _get_wpgmptr(wchar_t**)não. MSDN
Hydranix 5/16

Respostas:

181

Aqui está o código para obter o caminho completo para o aplicativo em execução:

Janelas:

int bytes = GetModuleFileName(NULL, pBuf, len);
return bytes ? bytes : -1;

Linux:

int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
if(bytes >= 0)
    pBuf[bytes] = '\0';
return bytes;
Desduplicador
fonte
3
Penso que esta é a única resposta aqui que responde à pergunta e o faz para Windows e Linux. Bom trabalho.
24568 Frank Szczerba
6
Boo para / proc / pid / exe - Não há suporte no OS X por algum motivo.
7897 Chris Lutz
24
Quando vejo código que olha para /procparte de mim morre um pouco. Todo o mundo não é o Linux, e até mesmo em que uma plataforma /procdevem ser considerados sujeitos a mudança de versão para versão, arco de arco, etc.
asveikau
4
se eles iniciarem usando um comando com alias no Linux, o argv [0] é o "nome do comando" ou expandido?
Andy Dent
20
Que tal adicionar char pBuf[256]; size_t len = sizeof(pBuf);para deixar a solução mais claramente.
Charles.cc.hsu
166

Se você buscar o diretório atual quando o programa for iniciado pela primeira vez, terá efetivamente o diretório do qual o programa foi iniciado. Armazene o valor em uma variável e consulte-o posteriormente no seu programa. Isso é diferente do diretório que contém o arquivo de programa executável atual . Não é necessariamente o mesmo diretório; se alguém executa o programa em um prompt de comando, o programa está sendo executado no diretório de trabalho atual do prompt de comando, mesmo que o arquivo do programa esteja em outro local.

getcwd é uma função POSIX e é suportada imediatamente por todas as plataformas compatíveis com POSIX. Você não precisaria fazer nada de especial (além de incluir os cabeçalhos corretos unistd.h no Unix e direct.h no Windows).

Como você está criando um programa C, ele será vinculado à biblioteca de tempo de execução c padrão, vinculada a TODOS os processos no sistema (exceções especialmente criadas, evitadas) e incluirá essa função por padrão. A CRT nunca é considerada uma biblioteca externa, pois fornece a interface compatível com o padrão básico para o sistema operacional.

No Windows, a função getcwd foi preterida em favor do _getcwd. Eu acho que você poderia usá-lo dessa maneira.

#include <stdio.h>  /* defines FILENAME_MAX */
#ifdef WINDOWS
    #include <direct.h>
    #define GetCurrentDir _getcwd
#else
    #include <unistd.h>
    #define GetCurrentDir getcwd
 #endif

 char cCurrentPath[FILENAME_MAX];

 if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
     {
     return errno;
     }

cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */

printf ("The current working directory is %s", cCurrentPath);
vida de computação
fonte
44
Boa resposta, mas pensei que "diretório de trabalho atual" não era o que se queria.
Michael Burr
4
você deve acrescentar que, mesmo se algumas documentações dizer que cCurrentpath pode ser nulo e será atribuído pelo getcwd getcwd não parece alocar algo no Mac OS e silenciosamente bate seu programa
Janusz
4
Há um pequeno erro, mas infelizmente eu não posso editar ainda .. linha 10: cCurrentpath: deve ser cCurrentPath
Lipis
8
Na IMO do Windows, as funções com o nome POSIXy (algumas das quais começando com sublinhados) geralmente devem ser evitadas. Eles não são as APIs reais do Windows, mas a CRT. A API do Windows que você deseja usar é GetCurrentDirectory (). msdn.microsoft.com/pt-br/library/aa364934(VS.85).aspx
asveikau
6
A resposta de Mike está correta. O "diretório atual" nem sempre é o mesmo que o diretório do qual o binário está sendo executado. Por exemplo, se um aplicativo é executado como um serviço no Windows, o diretório atual provavelmente será C: \ Windows \ System32, enquanto o diretório binário é diferente.
Lucky Luke
42

Isto é do fórum cplusplus

No Windows:

#include <string>
#include <windows.h>

std::string getexepath()
{
  char result[ MAX_PATH ];
  return std::string( result, GetModuleFileName( NULL, result, MAX_PATH ) );
}

No Linux:

#include <string>
#include <limits.h>
#include <unistd.h>

std::string getexepath()
{
  char result[ PATH_MAX ];
  ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
  return std::string( result, (count > 0) ? count : 0 );
}

No HP-UX:

#include <string>
#include <limits.h>
#define _PSTAT64
#include <sys/pstat.h>
#include <sys/types.h>
#include <unistd.h>

std::string getexepath()
{
  char result[ PATH_MAX ];
  struct pst_status ps;

  if (pstat_getproc( &ps, sizeof( ps ), 0, getpid() ) < 0)
    return std::string();

  if (pstat_getpathname( result, PATH_MAX, &ps.pst_fid_text ) < 0)
    return std::string();

  return std::string( result );
}
Polvo
fonte
1
Essa solução do Windows não manipula caracteres não-ANSI no caminho. Você provavelmente deve usar GetModuleFileNameW e convertê-lo em UTF-8 explicitamente (tome cuidado para convertê-lo novamente sempre que precisar emitir um comando do sistema de arquivos).
Adrian McCarthy
3
Para a solução do Windows, recebo o erro error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'ao compilar com o MinGW.
HelloGoodbye
2
@ Adrian, eu geralmente não sou um programador do Windows, mas não existe um DEFINE ou alguma maneira de dizer ao seu compilador para usar o sabor das funções _W () automaticamente?
Octopus
1
@ Octopus: Para usar as chamadas amplas, você precisará usar WCHAR (em vez de char) e std :: wstring (em vez de std :: string).
Adrian McCarthy
29

Se você deseja um caminho padrão sem bibliotecas: Não. Todo o conceito de diretório não está incluído no padrão.

Se você concorda que alguma dependência (portátil) de uma lib quase padrão está correta: Use a biblioteca do sistema de arquivos do Boost e solicite o initial_path () .

IMHO que é o mais próximo possível, com bom karma (o Boost é um conjunto de bibliotecas de alta qualidade bem estabelecido)

Thorsten79
fonte
8
Nos documentos do Boost: modelo <caminho da classe> const Path & initial_path (); Retorna: current_path () no momento da entrada em main (). E current_path () é 'como se por POSIX getcwd ()'. Não é isso que o questionador solicitou.
31416 Jonathan Leffler
veja boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/… para obter o boost 1.46.1
moala
Como comentado, isso fornece o caminho de onde o binário foi chamado, não o caminho do binário ... pois ele pode ser iniciado a partir de uma pasta diferente.
jpo38
21

O sistema de arquivos TS agora é um padrão (e suportado pelo gcc 5.3+ e clang 3.9+), para que você possa usar a current_path()função:

std::string path = std::experimental::filesystem::current_path();

No gcc (5.3+) para incluir o sistema de arquivos, você precisa usar:

#include <experimental/filesystem>

e vincule seu código a -lstdc++fs bandeira.

Se você deseja usar o sistema de arquivos com o Microsoft Visual Studio, leia isso .

Marqin
fonte
6
No link referenciado, 1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. Downvoted, como o OP pergunta especificamente sobre o caminho atual do executável em vez do diretório de trabalho atual.
315 S. Saad
20

Sei que é muito tarde para responder a essa pergunta, mas descobri que nenhuma das respostas era tão útil para mim quanto minha própria solução. Uma maneira muito simples de obter o caminho do seu CWD para a pasta bin é assim:

int main(int argc, char* argv[])
{
    std::string argv_str(argv[0]);
    std::string base = argv_str.substr(0, argv_str.find_last_of("/"));
}

Agora você pode apenas usar isso como base para o seu caminho relativo. Então, por exemplo, eu tenho essa estrutura de diretórios:

main
  ----> test
  ----> src
  ----> bin

e quero compilar meu código-fonte na lixeira e escrever um log para testar. Posso apenas adicionar essa linha ao meu código.

std::string pathToWrite = base + "/../test/test.log";

Eu tentei essa abordagem no Linux usando caminho completo, alias etc. e funciona muito bem.

NOTA:

Se você estiver no Windows, use um '\' como separador de arquivos, não '/'. Você terá que escapar disso também, por exemplo:

std::string base = argv[0].substr(0, argv[0].find_last_of("\\"));

Eu acho que isso deve funcionar, mas não foi testado, portanto, o comentário seria apreciado se funcionar ou uma correção, se não.

Sam Redway
fonte
Sim, também funciona no Windows. Eu acho que é a melhor solução. Tanto quanto eu sei argv [0] sempre mantém o caminho para o executável.
Wodzu
4
argv[0]é uma idéia muito boa, mas infelizmente o que estou recebendo no Linux é "./my_executable_name" ou "./make/my_executable_name". Basicamente o que eu recebo depende totalmente como eu lançá-lo
Xeverous
1
@ Xeverous: e daí? Se eu tiver alguns arquivos relativos ao meu executável que ele precisa abrir, a partir de "./" ou "./make/" nos seus casos deve funcionar. "." é o diretório de trabalho atual e argv [0] informará o caminho relativo para o executável a partir daí, que é exatamente o que o OP deve desejar. Em todo caso, é exatamente o que eu preciso.
nilo 22/03
9

Não, não existe um caminho padrão. Acredito que os padrões C / C ++ nem consideram a existência de diretórios (ou outras organizações de sistemas de arquivos).

No Windows, o GetModuleFileName () retornará o caminho completo para o arquivo executável do processo atual quando o parâmetro hModule estiver definido como NULL . Eu não posso ajudar com o Linux.

Além disso, você deve esclarecer se deseja o diretório atual ou o diretório em que a imagem / executável do programa reside. Tal como está, sua pergunta é um pouco ambígua nesse ponto.

Michael Burr
fonte
9

No Windows, a maneira mais simples é usar a _get_pgmptrfunção stdlib.hpara obter um ponteiro para uma string que representa o caminho absoluto para o executável, incluindo o nome dos executáveis.

char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe
Adam Yaxley
fonte
8

Talvez concatenar o diretório de trabalho atual com argv [0]? Não tenho certeza se isso funcionaria no Windows, mas funciona no Linux.

Por exemplo:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv) {
    char the_path[256];

    getcwd(the_path, 255);
    strcat(the_path, "/");
    strcat(the_path, argv[0]);

    printf("%s\n", the_path);

    return 0;
}

Quando executado, gera:

jeremy @ jeremy-desktop: ~ / Desktop $ ./test
/home/jeremy/Desktop/./test

Jeremy Ruten
fonte
Você precisará verificar se um caminho absoluto é fornecido em argv [0]. Mais importante, porém, e se a imagem estiver localizada no PATH? O linux preenche o caminho completo ou apenas o que está na linha de comando?
Michael Burr
Como Mike B apontou, essa é uma solução não geral; funciona apenas em algumas circunstâncias muito limitadas. Basicamente, somente quando você executa o comando por um nome de caminho relativo - e não é tão elegante quando você executa ../../../bin/progname em vez de ./test
Jonathan Leffler
Se você resolver o possível caminho relativo do argv [0] comparado ao diretório atual (porque o argv [0] pode ser "../../myprogram.exe"), essa é provavelmente a maneira mais segura de responder à pergunta. Ele sempre funcionará e é portátil (funciona até no Android!).
jpo38
7

Para Win32 GetCurrentDirectory deve fazer o truque.

Torbjörn Gyllebring
fonte
Há um grande problema com esta função : aplicativos multithread e código de biblioteca compartilhada não devem usar a função GetCurrentDirectory e devem evitar nomes de caminho relativos . Se você pode trabalhar com essa suposição, é a melhor solução.
McLeary 16/04
6

Você não pode usar argv [0] para esse fim, geralmente ele contém o caminho completo para o executável, mas não é necessário - processo pode ser criado com um valor arbitrário no campo.

Lembre-se também de que o diretório atual e o diretório com o executável são duas coisas diferentes, portanto, getcwd () também não ajudará.

No Windows, use GetModuleFileName (), nos arquivos de leitura / dev / proc / procID / .. do Linux .

eugensk
fonte
3

Só para empilhar atrasado aqui, ...

não há solução padrão, porque as linguagens são independentes dos sistemas de arquivos subjacentes; portanto, como outros já disseram, o conceito de sistema de arquivos baseado em diretório está fora do escopo das linguagens c / c ++.

Além disso, você não deseja o diretório de trabalho atual, mas o diretório em que o programa está sendo executado, o que deve levar em conta como o programa chegou aonde está - ou seja, foi gerado como um novo processo através de um fork, etc. Para obter o diretório em que um programa está sendo executado, como as soluções demonstraram, é necessário que você obtenha essas informações das estruturas de controle de processo do sistema operacional em questão, que é a única autoridade nessa questão. Assim, por definição, é uma solução específica para o SO.

Minok
fonte
3

Para o sistema Windows no console, você pode usar o dircomando system ( ). E o console fornece informações sobre diretório e etc. Leia sobre o dircomando em cmd. Mas para sistemas tipo Unix, não sei ... Se esse comando for executado, leia o comando bash.lsnão exibe o diretório ...

Exemplo:

int main()
{
    system("dir");
    system("pause"); //this wait for Enter-key-press;
    return 0;
}
Alexey1993
fonte
2
#include <windows.h>
using namespace std;

// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
    const unsigned long maxDir = 260;
    char currentDir[maxDir];
    GetCurrentDirectory(maxDir, currentDir);
    return string(currentDir);
}
freezotic
fonte
1

Nas plataformas POSIX, você pode usar getcwd () .

No Windows, você pode usar _getcwd () , como o uso de getcwd () foi preterido.

Para bibliotecas padrão, se o Boost fosse padrão o suficiente para você, eu sugeriria o sistema de arquivos Boost ::, mas eles parecem ter removido a normalização do caminho da proposta. Você pode ter que esperar até que o TR2 esteja prontamente disponível para uma solução totalmente padrão.

Fruny
fonte
10
getcwd () não faz o que o interlocutor pediu.
27616 Jonathan Leffler
não é que a resposta aceita use getcwd () ou não estou apenas entendendo?
Sнаđошƒаӽ
Votei porque foi você quem veio com o que é considerado a resposta correta primeiro.
Arnaud
Essa resposta nem tenta resolver a questão. Vergonha em escrevê-lo.
HelloWorld
1

Para caminhos relativos, aqui está o que eu fiz. Estou ciente da idade dessa pergunta. Quero apenas contribuir com uma resposta mais simples que funcione na maioria dos casos:

Digamos que você tenha um caminho como este:

"path/to/file/folder"

Por alguma razão, os executáveis ​​construídos no Linux e criados no eclipse funcionam bem com isso. No entanto, o Windows fica muito confuso se for fornecido um caminho como este para trabalhar!

Como mencionado acima, existem várias maneiras de obter o caminho atual para o executável, mas a maneira mais fácil que eu acho interessante funciona na maioria dos casos, é anexá-lo à FRENTE do seu caminho:

"./path/to/file/folder"

Basta adicionar "./" para classificar! :) Então você pode começar a carregar a partir de qualquer diretório que desejar, desde que seja com o próprio executável.

EDIT: Isso não funcionará se você tentar iniciar o executável a partir do code :: blocks se esse for o ambiente de desenvolvimento que está sendo usado, pois, por alguma razão, o code :: blocks não carrega as coisas corretamente ...: D

EDIT2: Algumas coisas novas que descobri é que, se você especificar um caminho estático como este em seu código (supondo Example.data é algo que você precisa carregar):

"resources/Example.data"

Se você iniciar o aplicativo a partir do diretório real (ou no Windows, criar um atalho e definir o diretório de trabalho como o diretório do aplicativo), ele funcionará dessa maneira. Lembre-se disso ao depurar problemas relacionados a caminhos de recursos / arquivos ausentes. (Especialmente nos IDEs que definem o diretório de trabalho errado ao iniciar um exe de compilação a partir do IDE)

FuzzyQuills
fonte
1

Uma solução de biblioteca (embora eu saiba que isso não foi solicitado). Se você usa o Qt: QCoreApplication::applicationDirPath()

Joachim
fonte
1

Apenas meus dois centavos, mas o código a seguir não funciona de maneira portável no C ++ 17?

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main(int argc, char* argv[])
{
    std::cout << "Path is " << fs::path(argv[0]).parent_path() << '\n';
}

Parece funcionar para mim no Linux, pelo menos.

Com base na ideia anterior, agora tenho:

std::filesystem::path prepend_exe_path(const std::string& filename, const std::string& exe_path = "");

Com implementação:

fs::path prepend_exe_path(const std::string& filename, const std::string& exe_path)
{
    static auto exe_parent_path = fs::path(exe_path).parent_path();
    return exe_parent_path / filename;
}

E truque de inicialização em main():

(void) prepend_exe_path("", argv[0]);

Obrigado @ Sam Redway pela idéia argv [0]. E, claro, entendo que o C ++ 17 não existia há muitos anos quando o OP fez a pergunta.

nilo
fonte
0

O impulso do sistema de arquivos initial_path()se comporta como o POSIX getcwd(), e nem o que você deseja por si só, mas anexandoargv[0] a um deles deve fazê-lo.

Você pode notar que o resultado nem sempre é bonito - você pode obter coisas como /foo/bar/../../baz/a.outou/foo/bar//baz/a.out , mas acredito que sempre resulta em um caminho válido que nomeia o executável (observe que barras consecutivas em um caminho são recolhidas para um).

Eu escrevi anteriormente uma solução usando envp(o terceiro argumento no main()qual funcionava no Linux, mas não parecia viável no Windows, por isso estou recomendando essencialmente a mesma solução que outra pessoa anteriormente, mas com a explicação adicional de por que ela está realmente correta mesmo que os resultados não sejam bonitos.

John Zwinck
fonte
0

Como Minok mencionou, não existe essa funcionalidade especificada no padrão C ou no padrão C ++. Este é considerado um recurso puramente específico do SO e é especificado no padrão POSIX, por exemplo.

Thorsten79 deu uma boa sugestão, é a biblioteca Boost.Filesystem. No entanto, pode ser inconveniente caso você não queira ter nenhuma dependência de tempo do link em formato binário para o seu programa.

Uma boa alternativa que eu recomendaria é a coleção de bibliotecas STLSoft C ++ 100% apenas para cabeçalhos, Matthew Wilson (autor de livros sobre leitura obrigatória sobre C ++). Há uma fachada portátil que PlatformSTL dá acesso à API específica do sistema: WinSTL para Windows e UnixSTL no Unix, por isso é uma solução portátil. Todos os elementos específicos do sistema são especificados com o uso de características e políticas, portanto, é uma estrutura extensível. Existe uma biblioteca do sistema de arquivos fornecida, é claro.

mloskot
fonte
0

O comando linux bash que progname reportará um caminho para o programa.

Mesmo se alguém puder emitir o comando de dentro do seu programa e direcionar a saída para um arquivo tmp e o programa posteriormente ler esse arquivo tmp, ele não informará se esse programa é o que está sendo executado. Apenas informa onde está localizado um programa com esse nome.

O que é necessário é obter o número de identificação do processo e analisar o caminho para o nome

No meu programa, quero saber se o programa foi executado no diretório bin do usuário ou de outro no caminho ou em / usr / bin. / usr / bin conteria a versão suportada. Meu sentimento é que, no Linux, existe a única solução que é portátil.

Leslie Satenstein
fonte
0

Use realpath()em stdlib.hcomo este:

char *working_dir_path = realpath(".", NULL);
Manabu Nakazawa
fonte
0

Funciona com a partir do C ++ 11, usando o sistema de arquivos experimental, e do C ++ 14-C ++ 17, bem como do sistema de arquivos oficial.

application.h:

#pragma once

//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>

namespace std {
    namespace filesystem = experimental::filesystem;
}
#endif

std::filesystem::path getexepath();

application.cpp:

#include "application.h"
#ifdef _WIN32
#include <windows.h>    //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h>     //readlink
#endif

std::filesystem::path getexepath()
{
#ifdef _WIN32
    wchar_t path[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, path, MAX_PATH);
    return path;
#else
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
#endif
}
TarmoPikaro
fonte
Boa resposta, mas é um comportamento indefinido adicionar declarações ou definições ao espaço para nomestd . Para evitar isso, você pode adicionar os espaços para nome std::filesysteme std::experimental::filesystemum terceiro espaço para nome de sua escolha, ou apenas usar using std::filesystem::path, se não se importar em adicionar a declaração pathao espaço para nome global.
Cássio Renan
Eu acho que depois do C ++ 14 experimental :: filesystem não é mais usado, então você pode esquecer isso? (entra no primeiro ramo #if)
TarmoPikaro