Come posso ottenere la directory da cui è in esecuzione un programma?


269

Esiste un metodo indipendente dalla piattaforma e indipendente dal filesystem per ottenere il percorso completo della directory da cui è in esecuzione un programma usando C / C ++? Da non confondere con la directory di lavoro corrente. (Si prega di non suggerire librerie a meno che non siano standard come clib o STL.)

(Se non esiste un metodo indipendente dalla piattaforma / filesystem, anche i suggerimenti che funzionano in Windows e Linux per filesystem specifici sono i benvenuti.)


@chakrit: Sarebbe fantastico. (Anche se quel problema di solito non si presenta sotto Windows.)
Ashwin Nanjappa,

2
A meno che non sia possibile estrarre in modo affidabile il percorso da argv[0], la tecnica sarà molto dipendente dal sistema operativo.
David R Tribble,

1
Solo per chiarire: la 'directory corrente', o, 'la directory da cui è in esecuzione il programma' (nella terminologia della domanda) è la directory in cui si trova il file di immagine del programma (file ~ .exe), e la ' directory di lavoro corrente ' è la directory, che viene completata automaticamente se il programma utilizza percorsi relativi?
Colemik,

3
Quando si #include <windows.h>, Windows inserisce automaticamente char*a nel percorso eseguibile _pgmptr. Non è necessario chiamare funzioni extra o assumere junk se si lavora solo su Windows.
rsethc,

1
Anche se il commento è di tre anni fa, vorrei ampliare il commento di rsethc _pgmptr. La documentazione MSDN afferma che le variabili _pgmptre _wpgmptrsono obsolete e si dovrebbe usare la funzione _get_pgmptr(char**)o _get_wpgmptr(wchar_t**)invece. MSDN
Hydranix

Risposte:


181

Ecco il codice per ottenere il percorso completo dell'app in esecuzione:

Finestre:

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;

3
Penso che questa sia l'unica risposta qui che risponda alla domanda, e lo fa sia per Windows che per Linux. Bel lavoro.
Frank Szczerba,

6
Boo per / proc / pid / exe - Non supportato su OS X per qualche motivo.
Chris Lutz,

24
Quando vedo il codice che guarda una /procparte di me muore un po '. Tutto il mondo non è Linux, e anche su quella piattaforma /procdovrebbe essere considerato soggetto a cambiamenti da versione a versione, da arco ad arco, ecc.
asveikau,

4
se si avviano utilizzando un comando con alias su Linux, argv [0] è il "nome del comando" o espanso?
Andy Dent,

20
Che ne dici di aggiungere char pBuf[256]; size_t len = sizeof(pBuf);per rendere la soluzione più chiara.
charles.cc.hsu,

166

Se recuperi la directory corrente all'avvio del programma, hai effettivamente la directory da cui è stato avviato il programma. Memorizza il valore in una variabile e fai riferimento ad esso più avanti nel tuo programma. Questo è distinto dalla directory che contiene il file di programma eseguibile corrente . Non è necessariamente la stessa directory; se qualcuno esegue il programma da un prompt dei comandi, il programma viene eseguito dalla directory di lavoro corrente del prompt dei comandi anche se il file di programma risiede altrove.

getcwd è una funzione POSIX e supportata immediatamente da tutte le piattaforme conformi a POSIX. Non dovresti fare nulla di speciale (a parte l'inclusione delle intestazioni giuste unistd.h su Unix e direct.h su windows).

Poiché si sta creando un programma C, esso si collegherà alla libreria di runtime c predefinita a cui sono collegati TUTTI i processi nel sistema (evitate eccezioni appositamente predisposte) e includerà questa funzione per impostazione predefinita. Il CRT non è mai considerato una libreria esterna perché fornisce al sistema operativo l'interfaccia conforme allo standard di base.

Su windows la funzione getcwd è stata deprecata a favore di _getcwd. Penso che potresti usarlo in questo modo.

#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);

44
Buona risposta, ma pensavo che "l'attuale directory di lavoro" non fosse ciò che si voleva.
Michael Burr,

4
dovresti aggiungere che anche se alcune documentazioni affermano che cCurrentpath può essere nullo e verrà allocato da getcwd getcwd non sembra allocare qualcosa su Mac OS e arresta in modo silenzioso il tuo programma
Janusz,

4
C'è un piccolo errore, ma sfortunatamente non riesco ancora a modificare .. linea 10: cCurrentpath: dovrebbe essere cCurrentPath
Lipis il

8
IMO su Windows le funzioni con nome POSIXy (alcune delle quali iniziano con caratteri di sottolineatura) dovrebbero essere generalmente evitate. Non sono le vere API di Windows ma piuttosto il CRT. L'API di Windows che desideri utilizzare è GetCurrentDirectory (). msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
asveikau

6
La risposta di Mike è corretta. La "directory corrente" non è sempre la stessa della directory da cui è in esecuzione il binario. Ad esempio, se un'app viene eseguita come servizio su Windows, la directory corrente sarà probabilmente C: \ Windows \ System32, mentre la directory binaria è diversa.
Lucky Luke,

42

Questo proviene dal forum cplusplus

Su Windows:

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

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

Su 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 );
}

Su 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 );
}

1
Quella soluzione di Windows non gestirà caratteri non ANSI nel percorso. Probabilmente dovresti usare GetModuleFileNameW e convertirlo esplicitamente in UTF-8 (facendo attenzione a riconvertirlo ogni volta che devi emettere un comando di filesystem).
Adrian McCarthy,

3
Per la soluzione Windows, ottengo l'errore error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'durante la compilazione con MinGW.
Ciao Arrivederci

2
@Adrian, in genere non sono un programmatore di Windows, ma non esiste un DEFINE o in qualche modo dire al compilatore di utilizzare automaticamente il sapore _W () delle funzioni?
Polpo

1
@Octopus: per usare le chiamate wide, dovresti usare WCHAR (invece di char) e std :: wstring (invece di std :: string).
Adrian McCarthy,

29

Se si desidera un modo standard senza librerie: No. L'intero concetto di directory non è incluso nello standard.

Se sei d'accordo che una dipendenza (portatile) da una lib quasi standard va bene: usa la libreria del filesystem di Boost e richiedi il percorso iniziale () .

IMHO il più vicino possibile, con un buon karma (Boost è un set di librerie di alta qualità ben consolidato)


8
Dai documenti Boost: template <class Path> const Path & initial_path (); Restituisce: current_path () al momento dell'entrata in main (). E current_path () è 'come se da POSIX getcwd ()'. Non è quello che ha chiesto l'interrogante.
Jonathan Leffler,


Come commentato, questo dà il percorso da cui è stato invocato il binario, non il percorso al binario ... in quanto potrebbe essere avviato da una cartella diversa.
jpo38,

21

Il filesystem TS è ora uno standard (e supportato da gcc 5.3+ e clang 3.9+), quindi puoi usare la current_path()funzione da esso:

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

In gcc (5.3+) per includere il filesystem devi usare:

#include <experimental/filesystem>

e collega il tuo codice con -lstdc++fsflag.

Se si desidera utilizzare il filesystem con Microsoft Visual Studio, leggere questo .


6
Dal collegamento referenziato, 1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. Downvoted, poiché l'OP chiede specificamente il percorso corrente dell'eseguibile anziché la directory di lavoro corrente.
S. Saad,

20

So che è molto tardi per dare una risposta a questo, ma ho scoperto che nessuna delle risposte mi è stata utile come la mia soluzione. Un modo molto semplice per ottenere il percorso dal CWD alla cartella bin è il seguente:

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

Ora puoi semplicemente usarlo come base per il tuo percorso relativo. Quindi per esempio ho questa struttura di directory:

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

e voglio compilare il mio codice sorgente nel cestino e scrivere un registro per testare, posso solo aggiungere questa riga al mio codice.

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

Ho provato questo approccio su Linux usando il percorso completo, alias ecc. E funziona perfettamente.

NOTA:

Se sei su Windows dovresti usare un '\' come separatore di file non '/'. Dovrai scappare anche da questo, ad esempio:

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

Penso che questo dovrebbe funzionare ma non è stato testato, quindi il commento sarebbe apprezzato se funziona o una soluzione in caso contrario.


Sì, funziona anche su Windows. Penso che sia la soluzione migliore. Per quanto ne so argv [0] mantiene sempre il percorso dell'eseguibile.
Wodzu,

4
argv[0]è una bella idea, ma sfortunatamente quello che sto ottenendo su Linux è "./my_executable_name" o "./make/my_executable_name". Fondamentalmente quello che ottengo dipende completamente da come lo lancio
Xeverous,

1
@Xeverous: e allora? Se ho alcuni file relativi al mio eseguibile che deve aprire, a partire da "./" o "./make/" nei tuoi casi dovrebbe funzionare. "" è la directory di lavoro corrente e argv [0] ti dirà il percorso relativo all'eseguibile da lì, che è esattamente ciò che l'OP dovrebbe desiderare. Questo è esattamente quello di cui ho bisogno.
nilo,

9

No, non esiste un modo standard. Credo che gli standard C / C ++ non considerino nemmeno l'esistenza di directory (o altre organizzazioni di file system).

Su Windows GetModuleFileName () restituirà il percorso completo al file eseguibile del processo corrente quando il parametro hModule è impostato su NULL . Non posso fare a meno di Linux.

Inoltre, è necessario chiarire se si desidera la directory corrente o la directory in cui risiede l'immagine del programma / eseguibile. Allo stato attuale, la tua domanda è un po 'ambigua su questo punto.


9

Su Windows il modo più semplice è utilizzare la _get_pgmptrfunzione stdlib.hper ottenere un puntatore a una stringa che rappresenta il percorso assoluto dell'eseguibile, incluso il nome degli eseguibili.

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

8

Forse concatenare l'attuale directory di lavoro con argv [0]? Non sono sicuro che funzionerebbe in Windows ma funziona in Linux.

Per esempio:

#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 eseguito, genera:

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


Avrai bisogno di un controllo per vedere se in argv [0] viene fornito un percorso assoluto. Ma soprattutto, cosa succede se l'immagine si trova tramite il PERCORSO? Linux riempie il percorso completo o semplicemente cosa c'è nella riga di comando?
Michael Burr,

Come ha sottolineato Mike B, questa è una soluzione non generale; funziona solo in alcune circostanze molto limitate. Fondamentalmente, solo quando esegui il comando con un relativo percorso - e non è poi così elegante quando esegui ../../../bin/progname anziché ./test
Jonathan Leffler

Se risolvi il possibile percorso relativo di argv [0] rispetto alla directory corrente (perché argv [0] potrebbe essere "../../myprogram.exe"), è probabilmente il modo più sicuro per rispondere alla domanda. Funzionerà sempre ed è portatile (funziona anche su Android!).
jpo38,


6

Non è possibile utilizzare argv [0] a tale scopo, di solito contiene il percorso completo dell'eseguibile, ma non essenziale - il processo potrebbe essere creato con valore arbitrario nel campo.

Inoltre, la directory corrente e la directory con l'eseguibile sono due cose diverse, quindi getcwd () non ti aiuterà neanche.

Su Windows usa GetModuleFileName (), su Linux leggi i file / dev / proc / procID / ...


3

Solo per accumulare in ritardo qui, ...

non esiste una soluzione standard, poiché i linguaggi sono indipendenti dai file system sottostanti, quindi, come altri hanno già detto, il concetto di file system basato su directory non rientra nell'ambito dei linguaggi c / c ++.

Inoltre, non si desidera la directory di lavoro corrente, ma la directory in cui è in esecuzione il programma, che deve tenere conto di come il programma è arrivato dove si trova, ovvero è stato generato come un nuovo processo tramite un fork, ecc. Per ottenere la directory in cui è in esecuzione un programma, come hanno dimostrato le soluzioni, è necessario ottenere tali informazioni dalle strutture di controllo del processo del sistema operativo in questione, che è l'unica autorità su questa domanda. Quindi, per definizione, è una soluzione specifica per il sistema operativo.


3

Per il sistema Windows sulla console è possibile utilizzare il dircomando system ( ). E la console fornisce informazioni sulla directory e così via. Leggi il dircomando su cmd. Ma per i sistemi simili a Unix, non lo so ... Se questo comando viene eseguito, leggi il comando bash. lsnon visualizza la directory ...

Esempio:

int main()
{
    system("dir");
    system("pause"); //this wait for Enter-key-press;
    return 0;
}

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);
}

1

Su piattaforme POSIX, è possibile utilizzare getcwd () .

Su Windows, puoi usare _getcwd () , poiché l'uso di getcwd () è stato deprecato.

Per le librerie standard, se Boost fosse abbastanza standard per te, avrei suggerito Boost :: filesystem, ma sembra che abbiano rimosso la normalizzazione del percorso dalla proposta. Potrebbe essere necessario attendere fino a quando TR2 non sarà prontamente disponibile per una soluzione completamente standard.


10
getcwd () non fa ciò che l'interrogatore ha chiesto.
Jonathan Leffler,

non è che la risposta accettata usa getcwd () o non sto solo capendo?
Sнаđошƒаӽ,

Ho votato perché sei colui che è venuto prima con quella che è considerata la risposta corretta.
Arnaud,

Questa risposta non tenta nemmeno di rispondere alla domanda. Peccato per averlo scritto.
HelloWorld,

1

Per i percorsi relativi, ecco cosa ho fatto. Sono consapevole dell'età di questa domanda, voglio semplicemente contribuire con una risposta più semplice che funzioni nella maggior parte dei casi:

Supponi di avere un percorso come questo:

"path/to/file/folder"

Per qualche ragione, i file eseguibili basati su Linux realizzati in eclipse funzionano bene con questo. Tuttavia, Windows diventa molto confuso se viene fornito un percorso come questo con cui lavorare!

Come detto sopra ci sono diversi modi per ottenere il percorso corrente per l'eseguibile, ma il modo più semplice che trovo funzioni un incantesimo nella maggior parte dei casi è quello di aggiungere questo al FRONT del tuo percorso:

"./path/to/file/folder"

Basta aggiungere "./" per farti ordinare! :) Quindi puoi iniziare a caricare da qualsiasi directory desideri, purché sia ​​con l'eseguibile stesso.

EDIT: Questo non funzionerà se provi ad avviare l'eseguibile da code :: blocks se è l'ambiente di sviluppo in uso, come per qualche ragione, code :: blocks non carica roba nel modo giusto ...: D

EDIT2: Alcune nuove cose che ho scoperto è che se specifichi un percorso statico come questo nel tuo codice (Supponendo che Esempio.data sia qualcosa che devi caricare):

"resources/Example.data"

Se avvii quindi l'app dalla directory effettiva (o in Windows, crei un collegamento e imposti la directory di lavoro sulla directory dell'app), funzionerà così. Tienilo a mente quando esegui il debug di problemi relativi a percorsi di risorse / file mancanti. (Soprattutto negli IDE che impostano la directory di lavoro errata quando si avvia un exe build dall'IDE)


1

Una soluzione di biblioteca (anche se so che questo non è stato richiesto). Se ti capita di usare Qt: QCoreApplication::applicationDirPath()


1

Solo i miei due centesimi, ma il seguente codice non funziona in modo portabile in 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';
}

Sembra funzionare per me almeno su Linux.

Sulla base dell'idea precedente, ora ho:

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

Con implementazione:

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 trucco di inizializzazione in main():

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

Grazie @Sam Redway per l'idea argv [0]. E, naturalmente, capisco che C ++ 17 non esisteva da molti anni quando l'OP fece la domanda.


0

Potenzia il filesystem si initial_path()comporta come POSIX getcwd(), e non fa nemmeno quello che vuoi da solo, ma accodandoti argv[0]a uno di essi dovrebbe farlo.

Puoi notare che il risultato non è sempre carino - potresti ottenere cose come /foo/bar/../../baz/a.outo /foo/bar//baz/a.out, ma credo che porti sempre a un percorso valido che denomina l'eseguibile (nota che le barre consecutive in un percorso sono compresse in una).

In precedenza ho scritto una soluzione utilizzando envp(il terzo argomento a main()cui funzionava su Linux ma non sembrava fattibile su Windows, quindi essenzialmente sto raccomandando la stessa soluzione di qualcun altro in precedenza, ma con l'ulteriore spiegazione del perché è effettivamente corretta anche se i risultati non sono belli.


0

Come menzionato Minok , non esiste tale funzionalità specificata nello standard C o C ++. Questa è considerata una funzione puramente specifica del sistema operativo ed è specificata nello standard POSIX, ad esempio.

Thorsten79 ha dato buoni suggerimenti, è la libreria Boost.Filesystem. Tuttavia, potrebbe essere scomodo nel caso in cui non si desideri avere dipendenze del tempo di collegamento in forma binaria per il proprio programma.

Una buona alternativa che consiglierei è la raccolta di librerie STLSoft C ++ 100% solo per intestazioni Matthew Wilson (autore di libri da leggere sul C ++). La facciata portatile PlatformSTL consente di accedere all'API specifica del sistema: WinSTL per Windows e UnixSTL su Unix, quindi è una soluzione portatile. Tutti gli elementi specifici del sistema sono specificati con l'uso di tratti e criteri, quindi è un quadro estensibile. C'è una libreria di filesystem fornita, ovviamente.


0

Il comando linux bash quale progname riporterà un percorso al programma.

Anche se uno potrebbe emettere il comando quale all'interno del tuo programma e indirizzare l'output a un file tmp e il programma successivamente leggerà quel file tmp, non ti dirà se quel programma è quello in esecuzione. Ti dice solo dove si trova un programma con quel nome.

È necessario ottenere il numero ID del processo e analizzare il percorso del nome

Nel mio programma voglio sapere se il programma è stato eseguito dalla directory bin dell'utente o da un'altra nel percorso o da / usr / bin. / usr / bin conterrebbe la versione supportata. La mia sensazione è che in Linux esiste l'unica soluzione portatile.


0

Utilizzare realpath()in stdlib.hquesto modo:

char *working_dir_path = realpath(".", NULL);

0

Funziona a partire da C ++ 11, usando un filesystem sperimentale, e C ++ 14-C ++ 17 e usando un filesystem ufficiale.

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
}

Bella risposta, ma è un comportamento indefinito aggiungere dichiarazioni o definizioni allo spazio dei nomistd . Per evitarlo, è possibile aggiungere sia gli spazi dei nomi std::filesystemsia std::experimental::filesystema un terzo spazio dei nomi di propria scelta, oppure utilizzare semplicemente using std::filesystem::path, se non vi dispiace aggiungere la dichiarazione pathallo spazio dei nomi globale.
Cássio Renan,

Immagino che dopo il C ++ 14 sperimentale :: il filesystem non sia più usato, quindi puoi semplicemente dimenticartene? (entra nel primo ramo #if)
TarmoPikaro,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.