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

Viewed 548305

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.)

25 Answers

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;

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

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)

No, there's no standard way. I believe that the C/C++ standards don't even consider the existence of directories (or other file system organizations).

On Windows the GetModuleFileName() will return the full path to the executable file of the current process when the hModule parameter is set to NULL. I can't help with Linux.

Also you should clarify whether you want the current directory or the directory that the program image/executable resides. As it stands your question is a little ambiguous on this point.

Maybe concatenate the current working directory with argv[0]? I'm not sure if that would work in Windows but it works in linux.

For example:

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

When run, it outputs:

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

You can not use argv[0] for that purpose, usually it does contain full path to the executable, but not nessesarily - process could be created with arbitrary value in the field.

Also mind you, the current directory and the directory with the executable are two different things, so getcwd() won't help you either.

On Windows use GetModuleFileName(), on Linux read /dev/proc/procID/.. files.

Just my two cents, but doesn't the following code portably work 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';
}

Seems to work for me on Linux at least.

Based on the previous idea, I now have:

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

With implementation:

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

And initialization trick in main():

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

Thanks @Sam Redway for the argv[0] idea. And of course, I understand that C++17 was not around for many years when the OP asked the question.

Just to belatedly pile on here,...

there is no standard solution, because the languages are agnostic of underlying file systems, so as others have said, the concept of a directory based file system is outside the scope of the c / c++ languages.

on top of that, you want not the current working directory, but the directory the program is running in, which must take into account how the program got to where it is - ie was it spawned as a new process via a fork, etc. To get the directory a program is running in, as the solutions have demonstrated, requires that you get that information from the process control structures of the operating system in question, which is the only authority on this question. Thus, by definition, its an OS specific solution.

Works with starting from C++11, using experimental filesystem, and C++14-C++17 as well using official filesystem.

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
}

A library solution (although I know this was not asked for). If you happen to use Qt: QCoreApplication::applicationDirPath()

Path to the current .exe


#include <Windows.h>

std::wstring getexepathW()
{
    wchar_t result[MAX_PATH];
    return std::wstring(result, GetModuleFileNameW(NULL, result, MAX_PATH));
}

std::wcout << getexepathW() << std::endl;

//  -------- OR --------

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

std::cout << getexepathA() << std::endl;

On POSIX platforms, you can use getcwd().

On Windows, you may use _getcwd(), as use of getcwd() has been deprecated.

For standard libraries, if Boost were standard enough for you, I would have suggested Boost::filesystem, but they seem to have removed path normalization from the proposal. You may have to wait until TR2 becomes readily available for a fully standard solution.

Boost Filesystem's initial_path() behaves like POSIX's getcwd(), and neither does what you want by itself, but appending argv[0] to either of them should do it.

You may note that the result is not always pretty--you may get things like /foo/bar/../../baz/a.out or /foo/bar//baz/a.out, but I believe that it always results in a valid path which names the executable (note that consecutive slashes in a path are collapsed to one).

I previously wrote a solution using envp (the third argument to main() which worked on Linux but didn't seem workable on Windows, so I'm essentially recommending the same solution as someone else did previously, but with the additional explanation of why it is actually correct even if the results are not pretty.

As Minok mentioned, there is no such functionality specified ini C standard or C++ standard. This is considered to be purely OS-specific feature and it is specified in POSIX standard, for example.

Thorsten79 has given good suggestion, it is Boost.Filesystem library. However, it may be inconvenient in case you don't want to have any link-time dependencies in binary form for your program.

A good alternative I would recommend is collection of 100% headers-only STLSoft C++ Libraries Matthew Wilson (author of must-read books about C++). There is portable facade PlatformSTL gives access to system-specific API: WinSTL for Windows and UnixSTL on Unix, so it is portable solution. All the system-specific elements are specified with use of traits and policies, so it is extensible framework. There is filesystem library provided, of course.

Use realpath() in stdlib.h like this:

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

The following worked well for me on macOS 10.15.7

brew install boost

main.cpp

#include <iostream>
#include <boost/filesystem.hpp>

int main(int argc, char* argv[]){
  boost::filesystem::path p{argv[0]};
  p = absolute(p).parent_path();
  std::cout << p << std::endl;
  return 0;
}

Compiling

g++ -Wall -std=c++11 -l boost_filesystem main.cpp
Related