How do I get the directory that a program is running from?

543,217

Solution 1

Here's code to get the full path to the executing app:

Windows:

char pBuf[256];
size_t len = sizeof(pBuf); 
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;

Solution 2

If you fetch the current directory when your program first starts, then you effectively have the directory your program was started from. Store the value in a variable and refer to it later in your program. This is distinct from the directory that holds the current executable program file. It isn't necessarily the same directory; if someone runs the program from a command prompt, then the program is being run from the command prompt's current working directory even though the program file lives elsewhere.

getcwd is a POSIX function and supported out of the box by all POSIX compliant platforms. You would not have to do anything special (apart from incliding the right headers unistd.h on Unix and direct.h on windows).

Since you are creating a C program it will link with the default c run time library which is linked to by ALL processes in the system (specially crafted exceptions avoided) and it will include this function by default. The CRT is never considered an external library because that provides the basic standard compliant interface to the OS.

On windows getcwd function has been deprecated in favour of _getcwd. I think you could use it in this fashion.

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

Solution 3

This is from the cplusplus forum

On windows:

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

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

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

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

Solution 4

If you want a standard way without libraries: No. The whole concept of a directory is not included in the standard.

If you agree that some (portable) dependency on a near-standard lib is okay: Use Boost's filesystem library and ask for the initial_path().

IMHO that's as close as you can get, with good karma (Boost is a well-established high quality set of libraries)

Solution 5

I know it is very late at the day to throw an answer at this one but I found that none of the answers were as useful to me as my own solution. A very simple way to get the path from your CWD to your bin folder is like this:

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

You can now just use this as a base for your relative path. So for example I have this directory structure:

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

and I want to compile my source code to bin and write a log to test I can just add this line to my code.

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

I have tried this approach on Linux using full path, alias etc. and it works just fine.

NOTE:

If you are on windows you should use a '\' as the file separator not '/'. You will have to escape this too for example:

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

I think this should work but haven't tested, so comment would be appreciated if it works or a fix if not.

Share:
543,217

Related videos on Youtube

Markus Joschko
Author by

Markus Joschko

I work with GPUs on deep learning and computer vision.

Updated on February 27, 2022

Comments

  • Markus Joschko
    Markus Joschko over 2 years

    Is there a platform-agnostic and filesystem-agnostic method to obtain the full path of the directory from where a program is running using C/C++? Not to be confused with the current working directory. (Please don't suggest libraries unless they're standard ones like clib or STL.)

    (If there's no platform/filesystem-agnostic method, suggestions that work in Windows and Linux for specific filesystems are welcome too.)

    • David R Tribble
      David R Tribble over 14 years
      Unless you can reliably extract the path from argv[0], the technique is going to be very OS-dependent.
    • colemik
      colemik about 11 years
      Just to clarify: the 'current directory', or, 'the directory that the program is running from' (in the terminology of the question) is the directory where the image file of the program (~.exe file) is located, and the 'current working directory' is the directory, that is autocompleted if the program uses relative paths?
    • rsethc
      rsethc about 11 years
      When you #include <windows.h>, Windows automatically puts a char* to the executable path in _pgmptr. You don't need to call extra functions or assume junk if you are working on Windows only.
    • Gregory Pakosz
      Gregory Pakosz about 9 years
      Does that answer your original question? github.com/gpakosz/whereami
    • Hydranix
      Hydranix over 8 years
      Although the comment is from three years ago, I'd like to expand on rsethc's comment about _pgmptr. MSDN documentation states that the _pgmptr and _wpgmptr variables are deprecated, and you should use the function _get_pgmptr(char**) or _get_wpgmptr(wchar_t**) instead. MSDN
    • AirCraft Lover
      AirCraft Lover about 5 years
      I tried the Windows' version using Borland C++Builder 6, the pBuf and len parameters are not known. Undefined symbol. What headers should I include?
    • nilo
      nilo about 4 years
      If you have a modern compiler, you might want to check stackoverflow.com/a/60804126/3246135.
  • Michael Burr
    Michael Burr almost 16 years
    You'll need a check to see if an absolute path is given in argv[0]. But more importantly, what if the image is located via the PATH? Does linux fill in the full path or just what's on the command line?
  • Jonathan Leffler
    Jonathan Leffler almost 16 years
    As Mike B pointed out, that is a non-general solution; it works in some very limited circumstances only. Basically, only when you run the command by a relative pathname - and it isn't all that elegant when you run ../../../bin/progname instead of ./test
  • Jonathan Leffler
    Jonathan Leffler almost 16 years
    getcwd() does not do what the questioner asked.
  • Jonathan Leffler
    Jonathan Leffler almost 16 years
    From the Boost docs: template <class Path> const Path& initial_path(); Returns: current_path() at the time of entry to main(). And current_path() is 'as if by POSIX getcwd()'. This is not what the questioner requested.
  • Michael Burr
    Michael Burr almost 16 years
    Good answer, but I thought "current working directory" was not what was wanted.
  • Frank Szczerba
    Frank Szczerba over 15 years
    I think this is the only answer here that answers the question, and does so for both Windows and Linux. Nice job.
  • SHRISH M
    SHRISH M about 15 years
    you should add that even if some documentations say that cCurrentpath can be null and will be allocated by getcwd getcwd does not seem to allocate something on Mac OS and quietly crashes your program
  • andrew cooke
    andrew cooke over 14 years
    why has this been marked as the correct answer when the original poster acknowledges that it's not what they wanted?!
  • Joseph White
    Joseph White over 14 years
    There is a small error, but unfortunately I can't edit yet.. line 10: cCurrentpath: should be cCurrentPath
  • asveikau
    asveikau over 14 years
    When I see code that looks at /proc part of me dies a little. All the world is not Linux, and even on that one platform /proc should be considered subject to change from version to version, arch to arch, etc.
  • asveikau
    asveikau over 14 years
    IMO on Windows the POSIXy-named functions (some of which starting with underscores) should be generally avoided. They aren't the real Windows APIs but rather the CRT. The Windows API you want to use is GetCurrentDirectory(). msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
  • asveikau
    asveikau over 14 years
    By the way, a more portable way to get the path to your binary on Unix is to look at argv[0], though this can be forged by the process that launched you.
  • Michael Mrozek
    Michael Mrozek about 14 years
    @asveikau argv[0] shows the command used to launch the process, so it's only going to show the whole path if the user typed the whole path. If the binary is on their PATH they could just type foo to run it, or ./foo if they're in the current directory -- argv[0] would be insufficient either way
  • asveikau
    asveikau about 14 years
    @Michael Mrozek - These are cases that are easily detected. Imagine logic which does: if argv[0] begins with '/', treat as absolute path, otherwise check if it contains a '/', if so it's a relative path and you need to insert the current working directory, otherwise you need to search for your binary in $PATH. As I said, though, this isn't perfect -- I can call execve() with argv[0] as "omg ponies" and the execve() will still work.
  • Andy Dent
    Andy Dent almost 14 years
    if they launch using an aliased command on Linux is the argv[0] the "name of the command" or expanded?
  • moala
    moala about 13 years
  • Alec Jacobson
    Alec Jacobson about 12 years
    I needed to add #include <errno.h> and use char instead of TCHAR to get this to work.
  • allyourcode
    allyourcode almost 12 years
    Why would you divide by sizeof(TCHAR) ? Don't you want to divide by sizeof(char)? Also, isn't the size of char always supposed to be 1? If so, dividing would seem to be superfluous.
  • Jerome
    Jerome almost 12 years
    @allyourcode TCHAR takes the value char or wchar_t depending on whether you build using Ascii or Unicode. sizeof(wchar_t) is 2 on Windows.
  • colemik
    colemik about 11 years
    Just to clarify: the 'current directory' is the directory where the image file of the program (~.exe file) is located, and the 'current working directory' is the directory, that is autocompleted if the program uses relative paths?
  • Adrian McCarthy
    Adrian McCarthy over 10 years
    The Windows solution isn't checking the return value correctly (or is expecting the caller to do more error checking). Also, it's returning the full path to the executable. If you want just the directory (as the question poses), you then need to strip the file name from the end of the path.
  • Adrian McCarthy
    Adrian McCarthy over 10 years
    That Windows solution won't handle non-ANSI characters in the path. You probably should use GetModuleFileNameW and convert it to UTF-8 explicitly (being careful to convert it back whenever you need to issue a filesystem command).
  • HelloGoodbye
    HelloGoodbye over 10 years
    For the Windows solution, I get the error error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)' when compiling with MinGW.
  • Lucky Luke
    Lucky Luke over 10 years
    Mike's answer is correct. The "current directory" is not always as the same as the directory the binary is running from. E.g., if an app runs as a service on Windows, the current directory will probably be C:\Windows\System32, whereas the binary dir is different.
  • Octopus
    Octopus about 10 years
    @Adrian, Im not generally a windows programmer, but isn't there a DEFINE or someway to tell your compiler to use the _W() flavor of functions automatically?
  • Adrian McCarthy
    Adrian McCarthy about 10 years
    @Octopus: To use the wide calls, you'd need to use WCHAR (instead of char) and std::wstring (instead of std::string).
  • Sнаđошƒаӽ
    Sнаđошƒаӽ about 9 years
    is it not that the accepted answer uses getcwd(), or am I not just understanding?
  • Arnaud
    Arnaud about 9 years
    I upvoted because you are the one who came with what is considered the correct answer first.
  • IInspectable
    IInspectable about 8 years
    @LuckyLuke: Or when an application is launched through a shortcut (.lnk), that allows you to specify the current working directory. So querying the CWD before doing anything else in your process won't be helpful either. This answer simply doesn't address the question.
  • jpo38
    jpo38 about 8 years
    As commented, this gives path from where binary was invoked, not path to the binary...as it could be started from a different folder.
  • jpo38
    jpo38 about 8 years
    If you resolve possible relative path of argv[0] compared to current directory (because argv[0] could be "../../myprogram.exe"), that's probably the safest way to answer the question. It will always work and is portable (it even works on Android!).
  • charles.cc.hsu
    charles.cc.hsu almost 8 years
    How about add char pBuf[256]; size_t len = sizeof(pBuf); to let the solution more clearly.
  • mrDudePerson
    mrDudePerson over 7 years
    "Thank you, Mike, you are a hero!" is now proudly present somewhere in the project that I'm currently working on. I also wanted to express my gratitude 'in person', so: thank you, Mike, you are a hero! Yup, it's 3:30 AM.
  • Wodzu
    Wodzu over 7 years
    Yes, it works on Windows as well. I think that is the best solution. As far as I know argv[0] always keeps path to the executable.
  • S. Saad
    S. Saad about 7 years
    From the referrenced link, 1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. Downvoted, as the OP specifically asks about the current path of the executable rather than the current working directory.
  • Xeverous
    Xeverous about 7 years
    argv[0] is a very nice idea, but unfortunalety what I'm getting on Linux is "./my_executable_name" or "./make/my_executable_name". Basically what I get depends completely how I launch it
  • Salamandar
    Salamandar almost 7 years
    You should use "/proc/self/exe" instead of this string copy mojo. Apart from that, great answer !
  • SmallChess
    SmallChess over 6 years
    This answer doesn't even attempt to address the question. Shame on writing it.
  • Beyondo
    Beyondo about 6 years
    It should be _WIN32 instead of WINDOWS, WINDOWS is more like compiler specific.
  • Tim
    Tim about 6 years
    This may not be what the person asked for but it is the answer I searched Google for and wanted ^_^ (needed to debug some code and this works perfect for me) Thanks!
  • Cássio Renan
    Cássio Renan about 5 years
    Nice answer, but it is undefined behavior to add declarations or definitions to namespace std. To avoid this, you can add both the namespaces std::filesystem and std::experimental::filesystem to a third namespace of your choice, or just use using std::filesystem::path, if you don't mind adding the declaration of path to the global namespace.
  • TarmoPikaro
    TarmoPikaro about 5 years
    I guess after C++14 experimental::filesystem is not used anymore, so you can just forget about this ? (goes into first #if branch)
  • nilo
    nilo over 4 years
    @Xeverous: so what? If I have some files relative to my executable that it needs to open, starting from "./" or "./make/" in your cases should work. "." is the current working directory, and argv[0] will tell you the relative path to the executable from there, which is exactly what the OP should want. That is in any case exactly what I need.
  • McLeary
    McLeary about 4 years
    There is big catch with this function: Multithreaded applications and shared library code should not use the GetCurrentDirectory function and should avoid using relative pathnames. If you can work with this assumption than it is the best solution.
  • dxiv
    dxiv over 3 years
    This duplicates another previous answer and, unlike that one, will fail in ANSI builds because the unqualified GetModuleFileName will resolve to GetModuleFileNameA instead of GetModuleFileNameW.
  • The Oathman
    The Oathman over 3 years
    I have modified the answer to include both. Much love
  • alle_meije
    alle_meije almost 3 years
    very clean. great answer
  • brc-dd
    brc-dd almost 3 years
    Seems to work partially on Windows too (tested on PowerShell Core, Windows PowerShell, MinGW). Running from CMD, this prints a blank string.