Related
I know this question has been asked before but I still haven't seen a satisfactory answer, or a definitive "no, this cannot be done", so I'll ask again!
All I want to do is get the path to the currently running executable, either as an absolute path or relative to where the executable is invoked from, in a platform-independent fashion. I though boost::filesystem::initial_path was the answer to my troubles but that seems to only handle the 'platform-independent' part of the question - it still returns the path from which the application was invoked.
For a bit of background, this is a game using Ogre, which I'm trying to profile using Very Sleepy, which runs the target executable from its own directory, so of course on load the game finds no configuration files etc. and promptly crashes. I want to be able to pass it an absolute path to the configuration files, which I know will always live alongside the executable. The same goes for debugging in Visual Studio - I'd like to be able to run $(TargetPath) without having to set the working directory.
There is no cross platform way that I know.
For Linux: pass "/proc/self/exe" to std::filesystem::canonical or readlink.
Windows: pass NULL as the module handle to GetModuleFileName.
The boost::dll::program_location function is one of the best cross platform methods of getting the path of the running executable that I know of. The DLL library was added to Boost in version 1.61.0.
The following is my solution. I have tested it on Windows, Mac OS X, Solaris, Free BSD, and GNU/Linux.
It requires Boost 1.55.0 or greater. It uses the Boost.Filesystem library directly and the Boost.Locale library and Boost.System library indirectly.
src/executable_path.cpp
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
# include <boost/process.hpp>
#endif
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
# include <Windows.h>
#endif
#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>
namespace boost {
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector buf(1024, 0);
size_type size = buf.size();
bool havePath = false;
bool shouldContinue = true;
do
{
DWORD result = GetModuleFileNameA(nullptr, &buf[0], size);
DWORD lastError = GetLastError();
if (result == 0)
{
shouldContinue = false;
}
else if (result < size)
{
havePath = true;
shouldContinue = false;
}
else if (
result == size
&& (lastError == ERROR_INSUFFICIENT_BUFFER || lastError == ERROR_SUCCESS)
)
{
size *= 2;
buf.resize(size);
}
else
{
shouldContinue = false;
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
// On Microsoft Windows, there is no need to call boost::filesystem::canonical or
// boost::filesystem::path::make_preferred. The path returned by GetModuleFileNameA
// is the one we want.
std::string ret = &buf[0];
return ret;
}
#elif (BOOST_OS_MACOS)
# include <mach-o/dyld.h>
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
char_vector buf(1024, 0);
uint32_t size = static_cast<uint32_t>(buf.size());
bool havePath = false;
bool shouldContinue = true;
do
{
int result = _NSGetExecutablePath(&buf[0], &size);
if (result == -1)
{
buf.resize(size + 1);
std::fill(std::begin(buf), std::end(buf), 0);
}
else
{
shouldContinue = false;
if (buf.at(0) != 0)
{
havePath = true;
}
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#elif (BOOST_OS_SOLARIS)
# include <stdlib.h>
std::string executable_path(const char* argv0)
{
std::string ret = getexecname();
if (ret.empty())
{
return detail::executable_path_fallback(argv0);
}
boost::filesystem::path p(ret);
if (!p.has_root_directory())
{
boost::system::error_code ec;
p = boost::filesystem::canonical(
p, boost::filesystem::current_path(), ec);
if (ec.value() != boost::system::errc::success)
{
return detail::executable_path_fallback(argv0);
}
ret = p.make_preferred().string();
}
return ret;
}
#elif (BOOST_OS_BSD)
# include <sys/sysctl.h>
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
int mib[4]{0};
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
int result = sysctl(mib, 4, nullptr, &size, nullptr, 0);
if (-1 == result)
{
return detail::executable_path_fallback(argv0);
}
char_vector buf(size + 1, 0);
result = sysctl(mib, 4, &buf[0], &size, nullptr, 0);
if (-1 == result)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#elif (BOOST_OS_LINUX)
# include <unistd.h>
std::string executable_path(const char *argv0)
{
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector buf(1024, 0);
size_type size = buf.size();
bool havePath = false;
bool shouldContinue = true;
do
{
ssize_t result = readlink("/proc/self/exe", &buf[0], size);
if (result < 0)
{
shouldContinue = false;
}
else if (static_cast<size_type>(result) < size)
{
havePath = true;
shouldContinue = false;
size = result;
}
else
{
size *= 2;
buf.resize(size);
std::fill(std::begin(buf), std::end(buf), 0);
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#else
std::string executable_path(const char *argv0)
{
return detail::executable_path_fallback(argv0);
}
#endif
}
src/detail/executable_path_internals.cpp
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
# include <boost/process.hpp>
#endif
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
# include <Windows.h>
#endif
#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>
namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName)
{
if (varName.empty()) return "";
#if (BOOST_OS_BSD || BOOST_OS_CYGWIN || BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_SOLARIS)
char* value = std::getenv(varName.c_str());
if (!value) return "";
return value;
#elif (BOOST_OS_WINDOWS)
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector value(8192, 0);
size_type size = value.size();
bool haveValue = false;
bool shouldContinue = true;
do
{
DWORD result = GetEnvironmentVariableA(varName.c_str(), &value[0], size);
if (result == 0)
{
shouldContinue = false;
}
else if (result < size)
{
haveValue = true;
shouldContinue = false;
}
else
{
size *= 2;
value.resize(size);
}
} while (shouldContinue);
std::string ret;
if (haveValue)
{
ret = &value[0];
}
return ret;
#else
return "";
#endif
}
bool GetDirectoryListFromDelimitedString(
const std::string& str,
std::vector<std::string>& dirs)
{
typedef boost::char_separator<char> char_separator_type;
typedef boost::tokenizer<
boost::char_separator<char>, std::string::const_iterator,
std::string> tokenizer_type;
dirs.clear();
if (str.empty())
{
return false;
}
#if (BOOST_OS_WINDOWS)
const std::string os_pathsep(";");
#else
const std::string os_pathsep(":");
#endif
char_separator_type pathSep(os_pathsep.c_str());
tokenizer_type strTok(str, pathSep);
typename tokenizer_type::iterator strIt;
typename tokenizer_type::iterator strEndIt = strTok.end();
for (strIt = strTok.begin(); strIt != strEndIt; ++strIt)
{
dirs.push_back(*strIt);
}
if (dirs.empty())
{
return false;
}
return true;
}
std::string search_path(const std::string& file)
{
if (file.empty()) return "";
std::string ret;
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
{
namespace bp = boost::process;
boost::filesystem::path p = bp::search_path(file);
ret = p.make_preferred().string();
}
#endif
if (!ret.empty()) return ret;
// Drat! I have to do it the hard way.
std::string pathEnvVar = GetEnv("PATH");
if (pathEnvVar.empty()) return "";
std::vector<std::string> pathDirs;
bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs);
if (!getDirList) return "";
std::vector<std::string>::const_iterator it = pathDirs.cbegin();
std::vector<std::string>::const_iterator itEnd = pathDirs.cend();
for ( ; it != itEnd; ++it)
{
boost::filesystem::path p(*it);
p /= file;
if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
{
return p.make_preferred().string();
}
}
return "";
}
std::string executable_path_fallback(const char *argv0)
{
if (argv0 == nullptr) return "";
if (argv0[0] == 0) return "";
#if (BOOST_OS_WINDOWS)
const std::string os_sep("\\");
#else
const std::string os_sep("/");
#endif
if (strstr(argv0, os_sep.c_str()) != nullptr)
{
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
argv0, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
}
std::string ret = search_path(argv0);
if (!ret.empty())
{
return ret;
}
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
argv0, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
ret = p.make_preferred().string();
}
return ret;
}
}
}
include/boost/executable_path.hpp
#ifndef BOOST_EXECUTABLE_PATH_HPP_
#define BOOST_EXECUTABLE_PATH_HPP_
#pragma once
#include <string>
namespace boost {
std::string executable_path(const char * argv0);
}
#endif // BOOST_EXECUTABLE_PATH_HPP_
include/boost/detail/executable_path_internals.hpp
#ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#pragma once
#include <string>
#include <vector>
namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName);
bool GetDirectoryListFromDelimitedString(
const std::string& str,
std::vector<std::string>& dirs);
std::string search_path(const std::string& file);
std::string executable_path_fallback(const char * argv0);
}
}
#endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
I have a complete project, including a test application and CMake build files available at SnKOpen - /cpp/executable_path/trunk. This version is more complete than the version I provided here. It is also supports more platforms.
I have tested the application on all supported operating systems in the following four scenarios.
Relative path, executable in current directory: i.e. ./executable_path_test
Relative path, executable in another directory: i.e. ./build/executable_path_test
Full path: i.e. /some/dir/executable_path_test
Executable in path, file name only: i.e. executable_path_test
In all four scenarios, both the executable_path and executable_path_fallback functions work and return the same results.
Notes
This is an updated answer to this question. I updated the answer to take into consideration user comments and suggestions. I also added a link to a project in my SVN Repository.
This way uses boost + argv. You mentioned this may not be cross platform because it may or may not include the executable name. Well the following code should work around that.
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc,char** argv)
{
fs::path full_path( fs::initial_path<fs::path>() );
full_path = fs::system_complete( fs::path( argv[0] ) );
std::cout << full_path << std::endl;
//Without file name
std::cout << full_path.stem() << std::endl;
//std::cout << fs::basename(full_path) << std::endl;
return 0;
}
The following code gets the current working directory which may do what you need
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc,char** argv)
{
//current working directory
fs::path full_path( fs::current_path<fs::path>() );
std::cout << full_path << std::endl;
std::cout << full_path.stem() << std::endl;
//std::cout << fs::basepath(full_path) << std::endl;
return 0;
}
Note
Just realized that basename() was deprecated so had to switch to .stem()
C++17, windows, unicode, using filesystem new api:
#include "..\Project.h"
#include <filesystem>
using namespace std;
using namespace filesystem;
int wmain(int argc, wchar_t** argv)
{
auto dir = weakly_canonical(path(argv[0])).parent_path();
printf("%S", dir.c_str());
return 0;
}
(Important: Use wmain with wchar_t** - don't mix main with wchar_t**. For cmake projects enable unicode using add_definitions(-DUNICODE -D_UNICODE)).
Suspect this solution should be portable, but don't know how unicode is implemented on other OS's.
weakly_canonical is needed only if you use as Output Directory upper folder references ('..') to simplify path. If you don't use it - remove it.
If you're operating from dynamic link library (.dll /.so), then you might not have argv, then you can consider following solution:
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
}
I'm not sure about Linux, but try this for Windows:
#include <windows.h>
#include <iostream>
using namespace std ;
int main()
{
char ownPth[MAX_PATH];
// When NULL is passed to GetModuleHandle, the handle of the exe itself is returned
HMODULE hModule = GetModuleHandle(NULL);
if (hModule != NULL)
{
// Use GetModuleFileName() with module handle to get the path
GetModuleFileName(hModule, ownPth, (sizeof(ownPth)));
cout << ownPth << endl ;
system("PAUSE");
return 0;
}
else
{
cout << "Module handle is NULL" << endl ;
system("PAUSE");
return 0;
}
}
This is what I ended up with
The header file looks like this:
#pragma once
#include <string>
namespace MyPaths {
std::string getExecutablePath();
std::string getExecutableDir();
std::string mergePaths(std::string pathA, std::string pathB);
bool checkIfFileExists (const std::string& filePath);
}
Implementation
#if defined(_WIN32)
#include <windows.h>
#include <Shlwapi.h>
#include <io.h>
#define access _access_s
#endif
#ifdef __APPLE__
#include <libgen.h>
#include <limits.h>
#include <mach-o/dyld.h>
#include <unistd.h>
#endif
#ifdef __linux__
#include <limits.h>
#include <libgen.h>
#include <unistd.h>
#if defined(__sun)
#define PROC_SELF_EXE "/proc/self/path/a.out"
#else
#define PROC_SELF_EXE "/proc/self/exe"
#endif
#endif
namespace MyPaths {
#if defined(_WIN32)
std::string getExecutablePath() {
char rawPathName[MAX_PATH];
GetModuleFileNameA(NULL, rawPathName, MAX_PATH);
return std::string(rawPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char* exePath = new char[executablePath.length()];
strcpy(exePath, executablePath.c_str());
PathRemoveFileSpecA(exePath);
std::string directory = std::string(exePath);
delete[] exePath;
return directory;
}
std::string mergePaths(std::string pathA, std::string pathB) {
char combined[MAX_PATH];
PathCombineA(combined, pathA.c_str(), pathB.c_str());
std::string mergedPath(combined);
return mergedPath;
}
#endif
#ifdef __linux__
std::string getExecutablePath() {
char rawPathName[PATH_MAX];
realpath(PROC_SELF_EXE, rawPathName);
return std::string(rawPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char *executablePathStr = new char[executablePath.length() + 1];
strcpy(executablePathStr, executablePath.c_str());
char* executableDir = dirname(executablePathStr);
delete [] executablePathStr;
return std::string(executableDir);
}
std::string mergePaths(std::string pathA, std::string pathB) {
return pathA+"/"+pathB;
}
#endif
#ifdef __APPLE__
std::string getExecutablePath() {
char rawPathName[PATH_MAX];
char realPathName[PATH_MAX];
uint32_t rawPathSize = (uint32_t)sizeof(rawPathName);
if(!_NSGetExecutablePath(rawPathName, &rawPathSize)) {
realpath(rawPathName, realPathName);
}
return std::string(realPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char *executablePathStr = new char[executablePath.length() + 1];
strcpy(executablePathStr, executablePath.c_str());
char* executableDir = dirname(executablePathStr);
delete [] executablePathStr;
return std::string(executableDir);
}
std::string mergePaths(std::string pathA, std::string pathB) {
return pathA+"/"+pathB;
}
#endif
bool checkIfFileExists (const std::string& filePath) {
return access( filePath.c_str(), 0 ) == 0;
}
}
For windows:
GetModuleFileName - returns the exe path + exe filename
To remove filename
PathRemoveFileSpec
QT provides this with OS abstraction as QCoreApplication::applicationDirPath()
If using C++17 one can do the following to get the path to the executable.
#include <filesystem>
std::filesystem::path getExecutablePath()
{
return std::filesystem::canonical("/proc/self/exe");
}
The above answer has been tested on Debian 10 using G++ 9.3.0
This is a Windows specific way, but it is at least half of your answer.
GetThisPath.h
/// dest is expected to be MAX_PATH in length.
/// returns dest
/// TCHAR dest[MAX_PATH];
/// GetThisPath(dest, MAX_PATH);
TCHAR* GetThisPath(TCHAR* dest, size_t destSize);
GetThisPath.cpp
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
TCHAR* GetThisPath(TCHAR* dest, size_t destSize)
{
if (!dest) return NULL;
if (MAX_PATH > destSize) return NULL;
DWORD length = GetModuleFileName( NULL, dest, destSize );
PathRemoveFileSpec(dest);
return dest;
}
mainProgram.cpp
TCHAR dest[MAX_PATH];
GetThisPath(dest, MAX_PATH);
I would suggest using platform detection as preprocessor directives to change the implementation of a wrapper function that calls GetThisPath for each platform.
Using args[0] and looking for '/' (or '\\'):
#include <string>
#include <iostream> // to show the result
int main( int numArgs, char *args[])
{
// Get the last position of '/'
std::string aux(args[0]);
// get '/' or '\\' depending on unix/mac or windows.
#if defined(_WIN32) || defined(WIN32)
int pos = aux.rfind('\\');
#else
int pos = aux.rfind('/');
#endif
// Get the path and the name
std::string path = aux.substr(0,pos+1);
std::string name = aux.substr(pos+1);
// show results
std::cout << "Path: " << path << std::endl;
std::cout << "Name: " << name << std::endl;
}
EDITED:
If '/' does not exist, pos==-1 so the result is correct.
For Windows you can use GetModuleFilename().
For Linux see BinReloc (old, defunct URL) mirror of BinReloc in datenwolf's GitHub repositories.
This is probably the most natural way to do it, while covering most major desktop platforms. I am not certain, but I believe this should work with all the BSD's, not just FreeBSD, if you change the platform macro check to cover all of them. If I ever get around to installing Solaris, I'll be sure to add that platform to the supported list.
Features full UTF-8 support on Windows, which not everyone cares enough to go that far.
procinfo/win32/procinfo.cpp
#ifdef _WIN32
#include "../procinfo.h"
#include <windows.h>
#include <tlhelp32.h>
#include <cstddef>
#include <vector>
#include <cwchar>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
static inline string narrow(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
process_t ppid_from_pid(process_t pid) {
process_t ppid;
HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hp, &pe)) {
do {
if (pe.th32ProcessID == pid) {
ppid = pe.th32ParentProcessID;
break;
}
} while (Process32Next(hp, &pe));
}
CloseHandle(hp);
return ppid;
}
string path_from_pid(process_t pid) {
string path;
HANDLE hm = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
MODULEENTRY32W me = { 0 };
me.dwSize = sizeof(MODULEENTRY32W);
if (Module32FirstW(hm, &me)) {
do {
if (me.th32ProcessID == pid) {
path = narrow(me.szExePath);
break;
}
} while (Module32NextW(hm, &me));
}
CloseHandle(hm);
return path;
}
#endif
procinfo/macosx/procinfo.cpp
#if defined(__APPLE__) && defined(__MACH__)
#include "../procinfo.h"
#include <libproc.h>
using std::string;
string path_from_pid(process_t pid) {
string path;
char buffer[PROC_PIDPATHINFO_MAXSIZE];
if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) {
path = string(buffer) + "\0";
}
return path;
}
#endif
procinfo/linux/procinfo.cpp
#ifdef __linux__
#include "../procinfo.h"
#include <cstdlib>
using std::string;
using std::to_string;
string path_from_pid(process_t pid) {
string path;
string link = string("/proc/") + to_string(pid) + string("/exe");
char *buffer = realpath(link.c_str(), NULL);
path = buffer ? : "";
free(buffer);
return path;
}
#endif
procinfo/freebsd/procinfo.cpp
#ifdef __FreeBSD__
#include "../procinfo.h"
#include <sys/sysctl.h>
#include <cstddef>
using std::string;
using std::size_t;
string path_from_pid(process_t pid) {
string path;
size_t length;
// CTL_KERN::KERN_PROC::KERN_PROC_PATHNAME(pid)
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid };
if (sysctl(mib, 4, NULL, &length, NULL, 0) == 0) {
path.resize(length, '\0');
char *buffer = path.data();
if (sysctl(mib, 4, buffer, &length, NULL, 0) == 0) {
path = string(buffer) + "\0";
}
}
return path;
}
#endif
procinfo/procinfo.cpp
#include "procinfo.h"
#ifdef _WiN32
#include <process.h>
#endif
#include <unistd.h>
#include <cstddef>
using std::string;
using std::size_t;
process_t pid_from_self() {
#ifdef _WIN32
return _getpid();
#else
return getpid();
#endif
}
process_t ppid_from_self() {
#ifdef _WIN32
return ppid_from_pid(pid_from_self());
#else
return getppid();
#endif
}
string dir_from_pid(process_t pid) {
string fname = path_from_pid(pid);
size_t fp = fname.find_last_of("/\\");
return fname.substr(0, fp + 1);
}
string name_from_pid(process_t pid) {
string fname = path_from_pid(pid);
size_t fp = fname.find_last_of("/\\");
return fname.substr(fp + 1);
}
procinfo/procinfo.h
#ifdef _WiN32
#include <windows.h>
typedef DWORD process_t;
#else
#include <sys/types.h>
typedef pid_t process_t;
#endif
#include <string>
/* windows-only helper function */
process_t ppid_from_pid(process_t pid);
/* get current process process id */
process_t pid_from_self();
/* get parent process process id */
process_t ppid_from_self();
/* std::string possible_result = "C:\\path\\to\\file.exe"; */
std::string path_from_pid(process_t pid);
/* std::string possible_result = "C:\\path\\to\\"; */
std::string dir_from_pid(process_t pid);
/* std::string possible_result = "file.exe"; */
std::string name_from_pid(process_t pid);
This allows getting the full path to the executable of pretty much any process id, except on Windows there are some process's with security attributes which simply will not allow it, so wysiwyg, this solution is not perfect.
To address what the question was asking more precisely, you may do this:
procinfo.cpp
#include "procinfo/procinfo.h"
#include <iostream>
using std::string;
using std::cout;
using std::endl;
int main() {
cout << dir_from_pid(pid_from_self()) << endl;
return 0;
}
Build the above file structure with this command:
procinfo.sh
cd "${0%/*}"
g++ procinfo.cpp procinfo/procinfo.cpp procinfo/win32/procinfo.cpp procinfo/macosx/procinfo.cpp procinfo/linux/procinfo.cpp procinfo/freebsd/procinfo.cpp -o procinfo.exe
For downloading a copy of the files listed above:
git clone git://github.com/time-killer-games/procinfo.git
For more cross-platform process-related goodness:
https://github.com/time-killer-games/enigma-dev
See the readme for a list of most of the functions included.
As others mentioned, argv[0] is quite a nice solution, provided that the platform actually passes the executable path, which is surely not less probable than the OS being Windows (where WinAPI can help find the executable path). If you want to strip the string to only include the path to the directory where the executable resides, then using that path to find other application files (like game assets if your program is a game) is perfectly fine, since opening files is relative to the working directory, or, if provided, the root.
The following works as a quick and dirty solution, but note that it is far from being foolproof:
#include <iostream>
using namespace std ;
int main( int argc, char** argv)
{
cout << argv[0] << endl ;
return 0;
}
In case you need to handle unicode paths for Windows:
#include <Windows.h>
#include <iostream>
int wmain(int argc, wchar_t * argv[])
{
HMODULE this_process_handle = GetModuleHandle(NULL);
wchar_t this_process_path[MAX_PATH];
GetModuleFileNameW(NULL, this_process_path, sizeof(this_process_path));
std::wcout << "Unicode path of this app: " << this_process_path << std::endl;
return 0;
}
There are several answers recommending using GetModuleFileName on Windows. These answers have some shortcomings like:
The code should work for both UNICODE and ANSI versions
The path can be longer than MAX_PATH
GetModuleFileName function can fail and return 0
GetModuleFileName can return a relative executable name instead of a full name
GetModuleFileName can return a short path like C:\GIT-RE~1\TEST_G~1\test.exe
Let me provide an improved version, which takes into account the abovementioned points:
#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>
// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
// First we need to get a length of the full name string
const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
if (full_name_length == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
if (res == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// The full name has been successfully written to the buffer.
return std::basic_string<TCHAR>(full_name_buffer.get());
}
// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
// First we need to get a length of the long name string
const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
if (long_name_length == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
if (res == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// The long name has been successfully written to the buffer.
return std::basic_string<TCHAR>(long_name_buffer.get());
}
std::basic_string<TCHAR> get_current_executable_full_name()
{
DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that
// the path doesn't exceeds 260 characters
std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};
while (true) {
const auto bytes_written = GetModuleFileName(
NULL, path_buffer.get(), path_buffer_size);
const auto last_error = GetLastError();
if (bytes_written == 0) {
// GetModuleFileName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
if (last_error == ERROR_INSUFFICIENT_BUFFER) {
// There is not enough space in our buffer to fit the path.
// We need to increase the buffer and try again.
path_buffer_size *= 2;
path_buffer.reset(new TCHAR[path_buffer_size]);
continue;
}
// GetModuleFileName has successfully written the executable name to the buffer.
// Now we need to convert it to a full long name
std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
return get_long_name(full_name.c_str());
}
}
// Example of how this function can be used
int main()
{
#ifdef UNICODE
// If you use UNICODE version of WinApi
std::wstring exe_file_full_name = get_current_executable_full_name();
std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
// If you use ANSI version of WinApi
std::string exe_file_full_name = get_current_executable_full_name();
std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}
For Windows, you have the problem of how to strip the executable from the result of GetModuleFileName(). The Windows API call PathRemoveFileSpec() that Nate used for that purpose in his answer changed between Windows 8 and its predecessors. So how to remain compatible with both and safe? Luckily, there's C++17 (or Boost, if you're using an older compiler). I do this:
#include <windows.h>
#include <string>
#include <filesystem>
namespace fs = std::experimental::filesystem;
// We could use fs::path as return type, but if you're not aware of
// std::experimental::filesystem, you probably handle filenames
// as strings anyway in the remainder of your code. I'm on Japanese
// Windows, so wide chars are a must.
std::wstring getDirectoryWithCurrentExecutable()
{
int size = 256;
std::vector<wchar_t> charBuffer;
// Let's be safe, and find the right buffer size programmatically.
do {
size *= 2;
charBuffer.resize(size);
// Resize until filename fits. GetModuleFileNameW returns the
// number of characters written to the buffer, so if the
// return value is smaller than the size of the buffer, it was
// large enough.
} while (GetModuleFileNameW(NULL, charBuffer.data(), size) == size);
// Typically: c:/program files (x86)/something/foo/bar/exe/files/win64/baz.exe
// (Note that windows supports forward and backward slashes as path
// separators, so you have to be careful when searching through a path
// manually.)
// Let's extract the interesting part:
fs::path path(charBuffer.data()); // Contains the full path including .exe
return path.remove_filename() // Extract the directory ...
.w_str(); // ... and convert to a string.
}
SDL2 (https://www.libsdl.org/) library has two functions implemented across a wide spectrum of platforms:
SDL_GetBasePath
SDL_GetPrefPath
So if you don't want to reinvent the wheel... sadly, it means including the entire library, although it's got a quite permissive license and one could also just copy the code. Besides, it provides a lot of other cross-platform functionality.
I didn't read if my solution is already posted but on linux and osx you can read the 0 argument in your main function like this:
int main(int argument_count, char **argument_list) {
std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
std::size_t pos = currentWorkingDirectoryPath.rfind("/"); // position of "live" in str
currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);
In the first item of argument_list the name of the executable is integrated but removed by the code above.
Here my simple solution that works in both Windows and Linux, based on this solution and modified with this answer:
#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux
string replace(const string& s, const string& from, const string& to) {
string r = s;
int p = 0;
while((p=(int)r.find(from, p))!=string::npos) {
r.replace(p, from.length(), to);
p += (int)to.length();
}
return r;
}
string get_exe_path() { // returns path where executable is located
string path = "";
#if defined(_WIN32)
wchar_t wc[260] = {0};
GetModuleFileNameW(NULL, wc, 260);
wstring ws(wc);
transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
path = replace(path, "\\", "/");
#elif defined(__linux__)
char c[260];
int length = (int)readlink("/proc/self/exe", c, 260);
path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
return path.substr(0, path.rfind('/')+1);
}
This was my solution in Windows. It is called like this:
std::wstring sResult = GetPathOfEXE(64);
Where 64 is the minimum size you think the path will be. GetPathOfEXE calls itself recursively, doubling the size of the buffer each time until it gets a big enough buffer to get the whole path without truncation.
std::wstring GetPathOfEXE(DWORD dwSize)
{
WCHAR* pwcharFileNamePath;
DWORD dwLastError;
HRESULT hrError;
std::wstring wsResult;
DWORD dwCount;
pwcharFileNamePath = new WCHAR[dwSize];
dwCount = GetModuleFileNameW(
NULL,
pwcharFileNamePath,
dwSize
);
dwLastError = GetLastError();
if (ERROR_SUCCESS == dwLastError)
{
hrError = PathCchRemoveFileSpec(
pwcharFileNamePath,
dwCount
);
if (S_OK == hrError)
{
wsResult = pwcharFileNamePath;
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
return wsResult;
}
else if(S_FALSE == hrError)
{
wsResult = pwcharFileNamePath;
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
//there was nothing to truncate off the end of the path
//returning something better than nothing in this case for the user
return wsResult;
}
else
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
std::ostringstream oss;
oss << "could not get file name and path of executing process. error truncating file name off path. last error : " << hrError;
throw std::runtime_error(oss.str().c_str());
}
}
else if (ERROR_INSUFFICIENT_BUFFER == dwLastError)
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
return GetPathOfEXE(
dwSize * 2
);
}
else
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
std::ostringstream oss;
oss << "could not get file name and path of executing process. last error : " << dwLastError;
throw std::runtime_error(oss.str().c_str());
}
}
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
in Unix(including Linux) try 'which', in Windows try 'where'.
#include <stdio.h>
#define _UNIX
int main(int argc, char** argv)
{
char cmd[128];
char buf[128];
FILE* fp = NULL;
#if defined(_UNIX)
sprintf(cmd, "which %s > my.path", argv[0]);
#else
sprintf(cmd, "where %s > my.path", argv[0]);
#endif
system(cmd);
fp = fopen("my.path", "r");
fgets(buf, sizeof(buf), fp);
fclose(fp);
printf("full path: %s\n", buf);
unlink("my.path");
return 0;
}
As of C++17:
Make sure you include std filesystem.
#include <filesystem>
and now you can do this.
std::filesystem::current_path().string()
boost filesystem became part of the standard lib.
if you can't find it try to look under:
std::experimental::filesystem
How do you iterate through every file/directory recursively in standard C++?
From C++17 onward, the <filesystem> header, and range-for, you can simply do this:
#include <filesystem>
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
std::cout << dirEntry << std::endl;
As of C++17, std::filesystem is part of the standard library and can be found in the <filesystem> header (no longer "experimental").
In standard C++, technically there is no way to do this since standard C++ has no conception of directories. If you want to expand your net a little bit, you might like to look at using Boost.FileSystem. This has been accepted for inclusion in TR2, so this gives you the best chance of keeping your implementation as close as possible to the standard.
An example, taken straight from the website:
bool find_file( const path & dir_path, // in this directory,
const std::string & file_name, // search for this name,
path & path_found ) // placing path here if found
{
if ( !exists( dir_path ) ) return false;
directory_iterator end_itr; // default construction yields past-the-end
for ( directory_iterator itr( dir_path );
itr != end_itr;
++itr )
{
if ( is_directory(itr->status()) )
{
if ( find_file( itr->path(), file_name, path_found ) ) return true;
}
else if ( itr->leaf() == file_name ) // see below
{
path_found = itr->path();
return true;
}
}
return false;
}
If using the Win32 API you can use the FindFirstFile and FindNextFile functions.
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
For recursive traversal of directories you must inspect each WIN32_FIND_DATA.dwFileAttributes to check if the FILE_ATTRIBUTE_DIRECTORY bit is set. If the bit is set then you can recursively call the function with that directory. Alternatively you can use a stack for providing the same effect of a recursive call but avoiding stack overflow for very long path trees.
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 &&
wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"\\" + ffd.cFileName);
}
else {
files.push_back(path + L"\\" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;
}
int main(int argc, char* argv[])
{
vector<wstring> files;
if (ListFiles(L"F:\\cvsrepos", L"*", files)) {
for (vector<wstring>::iterator it = files.begin();
it != files.end();
++it) {
wcout << it->c_str() << endl;
}
}
return 0;
}
You can make it even simpler with the new C++11 range based for and Boost:
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
struct recursive_directory_range
{
typedef recursive_directory_iterator iterator;
recursive_directory_range(path p) : p_(p) {}
iterator begin() { return recursive_directory_iterator(p_); }
iterator end() { return recursive_directory_iterator(); }
path p_;
};
for (auto it : recursive_directory_range(dir_path))
{
std::cout << it << std::endl;
}
A fast solution is using C's Dirent.h library.
Working code fragment from Wikipedia:
#include <stdio.h>
#include <dirent.h>
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir: Path does not exist or could not be read.");
return -1;
}
while ((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
You can use std::filesystem::recursive_directory_iterator. But beware, this includes symbolic (soft) links. If you want to avoid them you can use is_symlink. Example usage:
size_t directory_size(const std::filesystem::path& directory)
{
size_t size{ 0 };
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
{
if (entry.is_regular_file() && !entry.is_symlink())
{
size += entry.file_size();
}
}
return size;
}
In addition to the above mentioned boost::filesystem you may want to examine wxWidgets::wxDir and Qt::QDir.
Both wxWidgets and Qt are open source, cross platform C++ frameworks.
wxDir provides a flexible way to traverse files recursively using Traverse() or a simpler GetAllFiles() function. As well you can implement the traversal with GetFirst() and GetNext() functions (I assume that Traverse() and GetAllFiles() are wrappers that eventually use GetFirst() and GetNext() functions).
QDir provides access to directory structures and their contents. There are several ways to traverse directories with QDir. You can iterate over the directory contents (including sub-directories) with QDirIterator that was instantiated with QDirIterator::Subdirectories flag. Another way is to use QDir's GetEntryList() function and implement a recursive traversal.
Here is sample code (taken from here # Example 8-5) that shows how to iterate over all sub directories.
#include <qapplication.h>
#include <qdir.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QDir currentDir = QDir::current();
currentDir.setFilter( QDir::Dirs );
QStringList entries = currentDir.entryList();
for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry)
{
std::cout << *entry << std::endl;
}
return 0;
}
Boost::filesystem provides recursive_directory_iterator, which is quite convenient for this task:
#include "boost/filesystem.hpp"
#include <iostream>
using namespace boost::filesystem;
recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
std::cout << *it << std::endl;
}
You can use ftw(3) or nftw(3) to walk a filesystem hierarchy in C or C++ on POSIX systems.
We are in 2019. We have filesystem standard library in C++. The Filesystem library provides facilities for performing operations on file systems and their components, such as paths, regular files, and directories.
There is an important note on this link if you are considering portability issues. It says:
The filesystem library facilities may be unavailable if a hierarchical file system is not accessible to the implementation, or if it does not provide the necessary capabilities. Some features may not be available if they are not supported by the underlying file system (e.g. the FAT filesystem lacks symbolic links and forbids multiple hardlinks). In those cases, errors must be reported.
The filesystem library was originally developed as boost.filesystem, was published as the technical specification ISO/IEC TS 18822:2015, and finally merged to ISO C++ as of C++17. The boost implementation is currently available on more compilers and platforms than the C++17 library.
#adi-shavit has answered this question when it was part of std::experimental and he has updated this answer in 2017. I want to give more details about the library and show more detailed example.
std::filesystem::recursive_directory_iterator is an LegacyInputIterator that iterates over the directory_entry elements of a directory, and, recursively, over the entries of all subdirectories. The iteration order is unspecified, except that each directory entry is visited only once.
If you don't want to recursively iterate over the entries of subdirectories, then directory_iterator should be used.
Both iterators returns an object of directory_entry. directory_entry has various useful member functions like is_regular_file, is_directory, is_socket, is_symlink etc. The path() member function returns an object of std::filesystem::path and it can be used to get file extension, filename, root name.
Consider the example below. I have been using Ubuntu and compiled it over the terminal using
g++ example.cpp --std=c++17 -lstdc++fs -Wall
#include <iostream>
#include <string>
#include <filesystem>
void listFiles(std::string path)
{
for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
if (!dirEntry.is_regular_file()) {
std::cout << "Directory: " << dirEntry.path() << std::endl;
continue;
}
std::filesystem::path file = dirEntry.path();
std::cout << "Filename: " << file.filename() << " extension: " << file.extension() << std::endl;
}
}
int main()
{
listFiles("./");
return 0;
}
You would probably be best with either boost or c++14's experimental filesystem stuff. IF you are parsing an internal directory (ie. used for your program to store data after the program was closed), then make an index file that has an index of the file contents. By the way, you probably would need to use boost in the future, so if you don't have it installed, install it! Second of all, you could use a conditional compilation, e.g.:
#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif
The code for each case is taken from https://stackoverflow.com/a/67336/7077165
#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir: Path does not exist or could not be read.");
return -1;
}
while ((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 &&
wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"\\" + ffd.cFileName);
}
else {
files.push_back(path + L"\\" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;
}
#endif
//so on and so forth.
You don't. The C++ standard has no concept of directories. It is up to the implementation to turn a string into a file handle. The contents of that string and what it maps to is OS dependent. Keep in mind that C++ can be used to write that OS, so it gets used at a level where asking how to iterate through a directory is not yet defined (because you are writing the directory management code).
Look at your OS API documentation for how to do this. If you need to be portable, you will have to have a bunch of #ifdefs for various OSes.
You need to call OS-specific functions for filesystem traversal, like open() and readdir(). The C standard does not specify any filesystem-related functions.
You don't. Standard C++ doesn't expose to concept of a directory. Specifically it doesn't give any way to list all the files in a directory.
A horrible hack would be to use system() calls and to parse the results. The most reasonable solution would be to use some kind of cross-platform library such as Qt or even POSIX.
On C++17 you can by this way :
#include <filesystem>
#include <iostream>
#include <vector>
namespace fs = std::filesystem;
int main()
{
std::ios_base::sync_with_stdio(false);
for (const auto &entry : fs::recursive_directory_iterator(".")) {
if (entry.path().extension() == ".png") {
std::cout << entry.path().string() << std::endl;
}
}
return 0;
}
If you are on Windows, you can use the FindFirstFile together with FindNextFile API. You can use FindFileData.dwFileAttributes to check if a given path is a file or a directory. If it's a directory, you can recursively repeat the algorithm.
Here, I have put together some code that lists all the files on a Windows machine.
http://dreams-soft.com/projects/traverse-directory
File tree walk ftw is a recursive way to wall the whole directory tree in the path. More details are here.
NOTE : You can also use fts that can skip hidden files like . or .. or .bashrc
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
printf("0%3o\t%s\n", status->st_mode&0777, name);
}
if (type == FTW_D && strcmp(".", name) != 0)
{
printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
output looks like following:
0755 ./Shivaji/
0644 ./Shivaji/20200516_204454.png
0644 ./Shivaji/20200527_160408.png
0644 ./Shivaji/20200527_160352.png
0644 ./Shivaji/20200520_174754.png
0644 ./Shivaji/20200520_180103.png
0755 ./Saif/
0644 ./Saif/Snapchat-1751229005.jpg
0644 ./Saif/Snapchat-1356123194.jpg
0644 ./Saif/Snapchat-613911286.jpg
0644 ./Saif/Snapchat-107742096.jpg
0755 ./Milind/
0644 ./Milind/IMG_1828.JPG
0644 ./Milind/IMG_1839.JPG
0644 ./Milind/IMG_1825.JPG
0644 ./Milind/IMG_1831.JPG
0644 ./Milind/IMG_1840.JPG
Let us say if you want to match a filename (example: searching for all the *.jpg, *.jpeg, *.png files.) for a specific needs, use fnmatch.
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <fnmatch.h>
static const char *filters[] = {
"*.jpg", "*.jpeg", "*.png"
};
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
int i;
for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
/* if the filename matches the filter, */
if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
printf("0%3o\t%s\n", status->st_mode&0777, name);
break;
}
}
}
if (type == FTW_D && strcmp(".", name) != 0)
{
//printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
Answers of getting all file names recursively with C++11 for Windows and Linux(with experimental/filesystem):
For Windows:
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
void getFiles_w(string path, vector<string>& files) {
intptr_t hFile = 0;
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
do {
if ((fileinfo.attrib & _A_SUBDIR)) {
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("/").append(fileinfo.name), files);
}
else {
files.push_back(p.assign(path).append("/").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
}
}
For Linux:
#include <experimental/filesystem>
bool getFiles(std::experimental::filesystem::path path, vector<string>& filenames) {
namespace stdfs = std::experimental::filesystem;
// http://en.cppreference.com/w/cpp/experimental/fs/directory_iterator
const stdfs::directory_iterator end{} ;
for (stdfs::directory_iterator iter{path}; iter != end ; ++iter) {
// http://en.cppreference.com/w/cpp/experimental/fs/is_regular_file
if (!stdfs::is_regular_file(*iter)) { // comment out if all names (names of directories tc.) are required
if (getFiles(iter->path(), filenames))
return true;
}
else {
filenames.push_back(iter->path().string()) ;
cout << iter->path().string() << endl;
}
}
return false;
}
Just remember to link -lstdc++fs when you compile it with g++ in Linux.
Employee Visual C++ and WIN API:
bool Parser::queryDIR(string dir_name) {
vector<string> sameLayerFiles;
bool ret = false;
string dir = "";
//employee wide char
dir = dir_name + "\\*.*";;
//employee WIN File API
WIN32_FIND_DATA fd;
WIN32_FIND_DATA fd_dir;
HANDLE hFind = ::FindFirstFile(getWC(dir.c_str()), &fd);
HANDLE hFind_dir = ::FindFirstFile(getWC(dir.c_str()), &fd_dir);
string str_subdir;
string str_tmp;
//recursive call for diving into sub-directories
do {
if ((fd_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
//ignore trival file node
while(true) {
FindNextFile(hFind_dir, &fd_dir);
str_tmp = wc2str(fd_dir.cFileName);
if (str_tmp.compare(".") && str_tmp.compare("..")){
break;
}
}
if ((fd_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
str_subdir = wc2str(fd_dir.cFileName);
ret = queryDIR(dir_name + "\\" + str_subdir);
}
}
} while(::FindNextFile(hFind_dir, &fd_dir));
//iterate same layer files
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
str_tmp = wc2str(fd.cFileName);
string fname = dir_name + "\\" + str_tmp;
sameLayerFiles.push_back(fname);
}
} while(::FindNextFile(hFind, &fd));
for (std::vector<string>::iterator it=sameLayerFiles.begin(); it!=sameLayerFiles.end(); it++) {
std::cout << "iterated file:" << *it << "..." << std::endl;
//Doing something with every file here
}
return true;
}
Hope my code can help :)
And you can see more details and program screen-shots on My GitHub
what is the best way of searching files on windows in c++. Should I use boost or there is a better way . I'm encountering some problems with building filesystem library. I found this:
#include <stdio.h>
#include <dir.h>
#include <string.h>
#define ALL_ATTS (FA_DIREC | FA_ARCH)
void walker(const char *, const char *);
void walker(const char *path, const char *findme)
{
struct ffblk finder;
unsigned int res;
chdir(path);
for (res = findfirst("*.*", &finder, ALL_ATTS); res == 0; res = findnext(&finder))
{
if (strcmp(finder.ff_name, ".") == 0) continue; /* current dir */
if (strcmp(finder.ff_name, "..") == 0) continue; /* parent dir */
/*
* If its a directory, examine it
* else compare the filename with the one we're looking for
*/
if (finder.ff_attrib & FA_DIREC)
{
char newpath[MAXPATH];
strcpy(newpath, path);
strcat(newpath, "\\");
strcat(newpath, finder.ff_name);
chdir(finder.ff_name);
walker(newpath, findme);
chdir("..");
}
else
{
if (strcmp(finder.ff_name, findme) == 0)
{
printf("Found in: %s\n", path);
}
}
}
}
int main(void)
{
const char *root = "\\";
char buf[BUFSIZ];
printf ("This program will find a file on the current drive.\n"
"Enter the name of the file to look for: ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin))
{
strtok(buf, "\n"); /* Remove the newline character */
walker(root, buf);
}
return(0);
}
But none of the versions of dir headers works ...
I've found boost::filesystem to work quite well, as long as you know what you are doing. The following works for me:
#include <iostream>
#include <string>
#include "boost/filesystem.hpp"
namespace fs = boost::filesystem;
int _tmain(int argc, _TCHAR* argv[])
{
const std::string start = "C:\\";
const std::string findme = "winsock.dll";
fs::recursive_directory_iterator end;
fs::recursive_directory_iterator rdi(start);
while(rdi != end)
{
const std::string path = rdi->path().string();
if(path.find(findme) != std::string::npos)
{
std::cout << path << std::endl;
}
try
{
++rdi; // various system directories can bork this
}
catch(fs::filesystem_error e)
{
rdi.no_push(); // don't try to recurse into it
++rdi;
}
}
return 0;
}
You may want to consider Win32 APIs like FindFirstFile,FindNextFile, etc.
There are some sample codes on MSDN, like this.
You may want to have a look at the recls library made by Matthew Wilson.
The code you've found looks like it uses functions specific to some particular "standard" library (and may even have been written for MS-DOS). Secondarily, it uses a depth-first search; for directory searching, I usually prefer a breadth-first search.
I'd try to use Windows desktop search and/or Windows Search as the first choices. These will use pre-built indexes for really fast results if the user has that enabled.
If that's not available, I'd use code for a breadth-first search, which looks roughly like this:
#include <windows.h>
#include <queue>
#include <sstream>
#include <iostream>
#include <algorithm>
// I think MS's names for some things are obnoxious.
const HANDLE HNULL = INVALID_HANDLE_VALUE;
const int A_DIR = FILE_ATTRIBUTE_DIRECTORY;
std::ostream &operator<<(std::ostream &os, FILETIME const &ft) {
SYSTEMTIME utc, lt;
FileTimeToSystemTime(&ft, &utc);
SystemTimeToTzSpecificLocalTime(NULL, &utc, <);
return os << lt.wHour << ":" << lt.wMinute << ":" << lt.wSecond << "." << lt.wMilliseconds;
}
void process(std::string const &path, WIN32_FIND_DATA const &file) {
std::cout << file.ftCreationTime << "\t" << path << file.cFileName << "\n";
}
void find_file(std::string const &folder_name, std::string const &fmask) {
HANDLE finder; // for FindFirstFile
WIN32_FIND_DATA file; // data about current file.
std::priority_queue<std::string, std::vector<std::string>, std::greater<std::string> > dirs;
dirs.push(folder_name); // start with passed directory
do {
std::string path = dirs.top();// retrieve directory to search
dirs.pop();
if (path[path.size()-1] != '\\') // normalize the name.
path += "\\";
std::string mask = path + fmask; // create mask for searching
// traverse a directory.
if (HNULL==(finder=FindFirstFile(mask.c_str(), &file))) {
continue;
}
do {
if (!(file.dwFileAttributes & A_DIR)) // print file names
process(path, file);
} while (FindNextFile(finder, &file));
FindClose(finder);
if (HNULL==(finder=FindFirstFile((path + "*").c_str(), &file)))
continue;
do {
if ((file.dwFileAttributes & A_DIR) && (file.cFileName[0] != '.'))
dirs.push(path + file.cFileName);
} while (FindNextFile(finder, &file));
FindClose(finder);
} while (!dirs.empty());
}
int main(int argc, char **argv) {
if (argc > 2)
find_file(argv[1], argv[2]);
else
find_file("C:\\", "*");
return 0;
}
I know this question has been asked before but I still haven't seen a satisfactory answer, or a definitive "no, this cannot be done", so I'll ask again!
All I want to do is get the path to the currently running executable, either as an absolute path or relative to where the executable is invoked from, in a platform-independent fashion. I though boost::filesystem::initial_path was the answer to my troubles but that seems to only handle the 'platform-independent' part of the question - it still returns the path from which the application was invoked.
For a bit of background, this is a game using Ogre, which I'm trying to profile using Very Sleepy, which runs the target executable from its own directory, so of course on load the game finds no configuration files etc. and promptly crashes. I want to be able to pass it an absolute path to the configuration files, which I know will always live alongside the executable. The same goes for debugging in Visual Studio - I'd like to be able to run $(TargetPath) without having to set the working directory.
There is no cross platform way that I know.
For Linux: pass "/proc/self/exe" to std::filesystem::canonical or readlink.
Windows: pass NULL as the module handle to GetModuleFileName.
The boost::dll::program_location function is one of the best cross platform methods of getting the path of the running executable that I know of. The DLL library was added to Boost in version 1.61.0.
The following is my solution. I have tested it on Windows, Mac OS X, Solaris, Free BSD, and GNU/Linux.
It requires Boost 1.55.0 or greater. It uses the Boost.Filesystem library directly and the Boost.Locale library and Boost.System library indirectly.
src/executable_path.cpp
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
# include <boost/process.hpp>
#endif
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
# include <Windows.h>
#endif
#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>
namespace boost {
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector buf(1024, 0);
size_type size = buf.size();
bool havePath = false;
bool shouldContinue = true;
do
{
DWORD result = GetModuleFileNameA(nullptr, &buf[0], size);
DWORD lastError = GetLastError();
if (result == 0)
{
shouldContinue = false;
}
else if (result < size)
{
havePath = true;
shouldContinue = false;
}
else if (
result == size
&& (lastError == ERROR_INSUFFICIENT_BUFFER || lastError == ERROR_SUCCESS)
)
{
size *= 2;
buf.resize(size);
}
else
{
shouldContinue = false;
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
// On Microsoft Windows, there is no need to call boost::filesystem::canonical or
// boost::filesystem::path::make_preferred. The path returned by GetModuleFileNameA
// is the one we want.
std::string ret = &buf[0];
return ret;
}
#elif (BOOST_OS_MACOS)
# include <mach-o/dyld.h>
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
char_vector buf(1024, 0);
uint32_t size = static_cast<uint32_t>(buf.size());
bool havePath = false;
bool shouldContinue = true;
do
{
int result = _NSGetExecutablePath(&buf[0], &size);
if (result == -1)
{
buf.resize(size + 1);
std::fill(std::begin(buf), std::end(buf), 0);
}
else
{
shouldContinue = false;
if (buf.at(0) != 0)
{
havePath = true;
}
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#elif (BOOST_OS_SOLARIS)
# include <stdlib.h>
std::string executable_path(const char* argv0)
{
std::string ret = getexecname();
if (ret.empty())
{
return detail::executable_path_fallback(argv0);
}
boost::filesystem::path p(ret);
if (!p.has_root_directory())
{
boost::system::error_code ec;
p = boost::filesystem::canonical(
p, boost::filesystem::current_path(), ec);
if (ec.value() != boost::system::errc::success)
{
return detail::executable_path_fallback(argv0);
}
ret = p.make_preferred().string();
}
return ret;
}
#elif (BOOST_OS_BSD)
# include <sys/sysctl.h>
std::string executable_path(const char* argv0)
{
typedef std::vector<char> char_vector;
int mib[4]{0};
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
int result = sysctl(mib, 4, nullptr, &size, nullptr, 0);
if (-1 == result)
{
return detail::executable_path_fallback(argv0);
}
char_vector buf(size + 1, 0);
result = sysctl(mib, 4, &buf[0], &size, nullptr, 0);
if (-1 == result)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#elif (BOOST_OS_LINUX)
# include <unistd.h>
std::string executable_path(const char *argv0)
{
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector buf(1024, 0);
size_type size = buf.size();
bool havePath = false;
bool shouldContinue = true;
do
{
ssize_t result = readlink("/proc/self/exe", &buf[0], size);
if (result < 0)
{
shouldContinue = false;
}
else if (static_cast<size_type>(result) < size)
{
havePath = true;
shouldContinue = false;
size = result;
}
else
{
size *= 2;
buf.resize(size);
std::fill(std::begin(buf), std::end(buf), 0);
}
} while (shouldContinue);
if (!havePath)
{
return detail::executable_path_fallback(argv0);
}
std::string path(&buf[0], size);
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
path, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
return detail::executable_path_fallback(argv0);
}
#else
std::string executable_path(const char *argv0)
{
return detail::executable_path_fallback(argv0);
}
#endif
}
src/detail/executable_path_internals.cpp
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
# include <boost/process.hpp>
#endif
#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
# include <Windows.h>
#endif
#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>
namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName)
{
if (varName.empty()) return "";
#if (BOOST_OS_BSD || BOOST_OS_CYGWIN || BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_SOLARIS)
char* value = std::getenv(varName.c_str());
if (!value) return "";
return value;
#elif (BOOST_OS_WINDOWS)
typedef std::vector<char> char_vector;
typedef std::vector<char>::size_type size_type;
char_vector value(8192, 0);
size_type size = value.size();
bool haveValue = false;
bool shouldContinue = true;
do
{
DWORD result = GetEnvironmentVariableA(varName.c_str(), &value[0], size);
if (result == 0)
{
shouldContinue = false;
}
else if (result < size)
{
haveValue = true;
shouldContinue = false;
}
else
{
size *= 2;
value.resize(size);
}
} while (shouldContinue);
std::string ret;
if (haveValue)
{
ret = &value[0];
}
return ret;
#else
return "";
#endif
}
bool GetDirectoryListFromDelimitedString(
const std::string& str,
std::vector<std::string>& dirs)
{
typedef boost::char_separator<char> char_separator_type;
typedef boost::tokenizer<
boost::char_separator<char>, std::string::const_iterator,
std::string> tokenizer_type;
dirs.clear();
if (str.empty())
{
return false;
}
#if (BOOST_OS_WINDOWS)
const std::string os_pathsep(";");
#else
const std::string os_pathsep(":");
#endif
char_separator_type pathSep(os_pathsep.c_str());
tokenizer_type strTok(str, pathSep);
typename tokenizer_type::iterator strIt;
typename tokenizer_type::iterator strEndIt = strTok.end();
for (strIt = strTok.begin(); strIt != strEndIt; ++strIt)
{
dirs.push_back(*strIt);
}
if (dirs.empty())
{
return false;
}
return true;
}
std::string search_path(const std::string& file)
{
if (file.empty()) return "";
std::string ret;
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
{
namespace bp = boost::process;
boost::filesystem::path p = bp::search_path(file);
ret = p.make_preferred().string();
}
#endif
if (!ret.empty()) return ret;
// Drat! I have to do it the hard way.
std::string pathEnvVar = GetEnv("PATH");
if (pathEnvVar.empty()) return "";
std::vector<std::string> pathDirs;
bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs);
if (!getDirList) return "";
std::vector<std::string>::const_iterator it = pathDirs.cbegin();
std::vector<std::string>::const_iterator itEnd = pathDirs.cend();
for ( ; it != itEnd; ++it)
{
boost::filesystem::path p(*it);
p /= file;
if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
{
return p.make_preferred().string();
}
}
return "";
}
std::string executable_path_fallback(const char *argv0)
{
if (argv0 == nullptr) return "";
if (argv0[0] == 0) return "";
#if (BOOST_OS_WINDOWS)
const std::string os_sep("\\");
#else
const std::string os_sep("/");
#endif
if (strstr(argv0, os_sep.c_str()) != nullptr)
{
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
argv0, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
return p.make_preferred().string();
}
}
std::string ret = search_path(argv0);
if (!ret.empty())
{
return ret;
}
boost::system::error_code ec;
boost::filesystem::path p(
boost::filesystem::canonical(
argv0, boost::filesystem::current_path(), ec));
if (ec.value() == boost::system::errc::success)
{
ret = p.make_preferred().string();
}
return ret;
}
}
}
include/boost/executable_path.hpp
#ifndef BOOST_EXECUTABLE_PATH_HPP_
#define BOOST_EXECUTABLE_PATH_HPP_
#pragma once
#include <string>
namespace boost {
std::string executable_path(const char * argv0);
}
#endif // BOOST_EXECUTABLE_PATH_HPP_
include/boost/detail/executable_path_internals.hpp
#ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#pragma once
#include <string>
#include <vector>
namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName);
bool GetDirectoryListFromDelimitedString(
const std::string& str,
std::vector<std::string>& dirs);
std::string search_path(const std::string& file);
std::string executable_path_fallback(const char * argv0);
}
}
#endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
I have a complete project, including a test application and CMake build files available at SnKOpen - /cpp/executable_path/trunk. This version is more complete than the version I provided here. It is also supports more platforms.
I have tested the application on all supported operating systems in the following four scenarios.
Relative path, executable in current directory: i.e. ./executable_path_test
Relative path, executable in another directory: i.e. ./build/executable_path_test
Full path: i.e. /some/dir/executable_path_test
Executable in path, file name only: i.e. executable_path_test
In all four scenarios, both the executable_path and executable_path_fallback functions work and return the same results.
Notes
This is an updated answer to this question. I updated the answer to take into consideration user comments and suggestions. I also added a link to a project in my SVN Repository.
This way uses boost + argv. You mentioned this may not be cross platform because it may or may not include the executable name. Well the following code should work around that.
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc,char** argv)
{
fs::path full_path( fs::initial_path<fs::path>() );
full_path = fs::system_complete( fs::path( argv[0] ) );
std::cout << full_path << std::endl;
//Without file name
std::cout << full_path.stem() << std::endl;
//std::cout << fs::basename(full_path) << std::endl;
return 0;
}
The following code gets the current working directory which may do what you need
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace fs = boost::filesystem;
int main(int argc,char** argv)
{
//current working directory
fs::path full_path( fs::current_path<fs::path>() );
std::cout << full_path << std::endl;
std::cout << full_path.stem() << std::endl;
//std::cout << fs::basepath(full_path) << std::endl;
return 0;
}
Note
Just realized that basename() was deprecated so had to switch to .stem()
C++17, windows, unicode, using filesystem new api:
#include "..\Project.h"
#include <filesystem>
using namespace std;
using namespace filesystem;
int wmain(int argc, wchar_t** argv)
{
auto dir = weakly_canonical(path(argv[0])).parent_path();
printf("%S", dir.c_str());
return 0;
}
(Important: Use wmain with wchar_t** - don't mix main with wchar_t**. For cmake projects enable unicode using add_definitions(-DUNICODE -D_UNICODE)).
Suspect this solution should be portable, but don't know how unicode is implemented on other OS's.
weakly_canonical is needed only if you use as Output Directory upper folder references ('..') to simplify path. If you don't use it - remove it.
If you're operating from dynamic link library (.dll /.so), then you might not have argv, then you can consider following solution:
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
}
I'm not sure about Linux, but try this for Windows:
#include <windows.h>
#include <iostream>
using namespace std ;
int main()
{
char ownPth[MAX_PATH];
// When NULL is passed to GetModuleHandle, the handle of the exe itself is returned
HMODULE hModule = GetModuleHandle(NULL);
if (hModule != NULL)
{
// Use GetModuleFileName() with module handle to get the path
GetModuleFileName(hModule, ownPth, (sizeof(ownPth)));
cout << ownPth << endl ;
system("PAUSE");
return 0;
}
else
{
cout << "Module handle is NULL" << endl ;
system("PAUSE");
return 0;
}
}
This is what I ended up with
The header file looks like this:
#pragma once
#include <string>
namespace MyPaths {
std::string getExecutablePath();
std::string getExecutableDir();
std::string mergePaths(std::string pathA, std::string pathB);
bool checkIfFileExists (const std::string& filePath);
}
Implementation
#if defined(_WIN32)
#include <windows.h>
#include <Shlwapi.h>
#include <io.h>
#define access _access_s
#endif
#ifdef __APPLE__
#include <libgen.h>
#include <limits.h>
#include <mach-o/dyld.h>
#include <unistd.h>
#endif
#ifdef __linux__
#include <limits.h>
#include <libgen.h>
#include <unistd.h>
#if defined(__sun)
#define PROC_SELF_EXE "/proc/self/path/a.out"
#else
#define PROC_SELF_EXE "/proc/self/exe"
#endif
#endif
namespace MyPaths {
#if defined(_WIN32)
std::string getExecutablePath() {
char rawPathName[MAX_PATH];
GetModuleFileNameA(NULL, rawPathName, MAX_PATH);
return std::string(rawPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char* exePath = new char[executablePath.length()];
strcpy(exePath, executablePath.c_str());
PathRemoveFileSpecA(exePath);
std::string directory = std::string(exePath);
delete[] exePath;
return directory;
}
std::string mergePaths(std::string pathA, std::string pathB) {
char combined[MAX_PATH];
PathCombineA(combined, pathA.c_str(), pathB.c_str());
std::string mergedPath(combined);
return mergedPath;
}
#endif
#ifdef __linux__
std::string getExecutablePath() {
char rawPathName[PATH_MAX];
realpath(PROC_SELF_EXE, rawPathName);
return std::string(rawPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char *executablePathStr = new char[executablePath.length() + 1];
strcpy(executablePathStr, executablePath.c_str());
char* executableDir = dirname(executablePathStr);
delete [] executablePathStr;
return std::string(executableDir);
}
std::string mergePaths(std::string pathA, std::string pathB) {
return pathA+"/"+pathB;
}
#endif
#ifdef __APPLE__
std::string getExecutablePath() {
char rawPathName[PATH_MAX];
char realPathName[PATH_MAX];
uint32_t rawPathSize = (uint32_t)sizeof(rawPathName);
if(!_NSGetExecutablePath(rawPathName, &rawPathSize)) {
realpath(rawPathName, realPathName);
}
return std::string(realPathName);
}
std::string getExecutableDir() {
std::string executablePath = getExecutablePath();
char *executablePathStr = new char[executablePath.length() + 1];
strcpy(executablePathStr, executablePath.c_str());
char* executableDir = dirname(executablePathStr);
delete [] executablePathStr;
return std::string(executableDir);
}
std::string mergePaths(std::string pathA, std::string pathB) {
return pathA+"/"+pathB;
}
#endif
bool checkIfFileExists (const std::string& filePath) {
return access( filePath.c_str(), 0 ) == 0;
}
}
For windows:
GetModuleFileName - returns the exe path + exe filename
To remove filename
PathRemoveFileSpec
QT provides this with OS abstraction as QCoreApplication::applicationDirPath()
If using C++17 one can do the following to get the path to the executable.
#include <filesystem>
std::filesystem::path getExecutablePath()
{
return std::filesystem::canonical("/proc/self/exe");
}
The above answer has been tested on Debian 10 using G++ 9.3.0
This is a Windows specific way, but it is at least half of your answer.
GetThisPath.h
/// dest is expected to be MAX_PATH in length.
/// returns dest
/// TCHAR dest[MAX_PATH];
/// GetThisPath(dest, MAX_PATH);
TCHAR* GetThisPath(TCHAR* dest, size_t destSize);
GetThisPath.cpp
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
TCHAR* GetThisPath(TCHAR* dest, size_t destSize)
{
if (!dest) return NULL;
if (MAX_PATH > destSize) return NULL;
DWORD length = GetModuleFileName( NULL, dest, destSize );
PathRemoveFileSpec(dest);
return dest;
}
mainProgram.cpp
TCHAR dest[MAX_PATH];
GetThisPath(dest, MAX_PATH);
I would suggest using platform detection as preprocessor directives to change the implementation of a wrapper function that calls GetThisPath for each platform.
Using args[0] and looking for '/' (or '\\'):
#include <string>
#include <iostream> // to show the result
int main( int numArgs, char *args[])
{
// Get the last position of '/'
std::string aux(args[0]);
// get '/' or '\\' depending on unix/mac or windows.
#if defined(_WIN32) || defined(WIN32)
int pos = aux.rfind('\\');
#else
int pos = aux.rfind('/');
#endif
// Get the path and the name
std::string path = aux.substr(0,pos+1);
std::string name = aux.substr(pos+1);
// show results
std::cout << "Path: " << path << std::endl;
std::cout << "Name: " << name << std::endl;
}
EDITED:
If '/' does not exist, pos==-1 so the result is correct.
For Windows you can use GetModuleFilename().
For Linux see BinReloc (old, defunct URL) mirror of BinReloc in datenwolf's GitHub repositories.
This is probably the most natural way to do it, while covering most major desktop platforms. I am not certain, but I believe this should work with all the BSD's, not just FreeBSD, if you change the platform macro check to cover all of them. If I ever get around to installing Solaris, I'll be sure to add that platform to the supported list.
Features full UTF-8 support on Windows, which not everyone cares enough to go that far.
procinfo/win32/procinfo.cpp
#ifdef _WIN32
#include "../procinfo.h"
#include <windows.h>
#include <tlhelp32.h>
#include <cstddef>
#include <vector>
#include <cwchar>
using std::string;
using std::wstring;
using std::vector;
using std::size_t;
static inline string narrow(wstring wstr) {
int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
vector<char> buf(nbytes);
return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}
process_t ppid_from_pid(process_t pid) {
process_t ppid;
HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hp, &pe)) {
do {
if (pe.th32ProcessID == pid) {
ppid = pe.th32ParentProcessID;
break;
}
} while (Process32Next(hp, &pe));
}
CloseHandle(hp);
return ppid;
}
string path_from_pid(process_t pid) {
string path;
HANDLE hm = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
MODULEENTRY32W me = { 0 };
me.dwSize = sizeof(MODULEENTRY32W);
if (Module32FirstW(hm, &me)) {
do {
if (me.th32ProcessID == pid) {
path = narrow(me.szExePath);
break;
}
} while (Module32NextW(hm, &me));
}
CloseHandle(hm);
return path;
}
#endif
procinfo/macosx/procinfo.cpp
#if defined(__APPLE__) && defined(__MACH__)
#include "../procinfo.h"
#include <libproc.h>
using std::string;
string path_from_pid(process_t pid) {
string path;
char buffer[PROC_PIDPATHINFO_MAXSIZE];
if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) {
path = string(buffer) + "\0";
}
return path;
}
#endif
procinfo/linux/procinfo.cpp
#ifdef __linux__
#include "../procinfo.h"
#include <cstdlib>
using std::string;
using std::to_string;
string path_from_pid(process_t pid) {
string path;
string link = string("/proc/") + to_string(pid) + string("/exe");
char *buffer = realpath(link.c_str(), NULL);
path = buffer ? : "";
free(buffer);
return path;
}
#endif
procinfo/freebsd/procinfo.cpp
#ifdef __FreeBSD__
#include "../procinfo.h"
#include <sys/sysctl.h>
#include <cstddef>
using std::string;
using std::size_t;
string path_from_pid(process_t pid) {
string path;
size_t length;
// CTL_KERN::KERN_PROC::KERN_PROC_PATHNAME(pid)
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid };
if (sysctl(mib, 4, NULL, &length, NULL, 0) == 0) {
path.resize(length, '\0');
char *buffer = path.data();
if (sysctl(mib, 4, buffer, &length, NULL, 0) == 0) {
path = string(buffer) + "\0";
}
}
return path;
}
#endif
procinfo/procinfo.cpp
#include "procinfo.h"
#ifdef _WiN32
#include <process.h>
#endif
#include <unistd.h>
#include <cstddef>
using std::string;
using std::size_t;
process_t pid_from_self() {
#ifdef _WIN32
return _getpid();
#else
return getpid();
#endif
}
process_t ppid_from_self() {
#ifdef _WIN32
return ppid_from_pid(pid_from_self());
#else
return getppid();
#endif
}
string dir_from_pid(process_t pid) {
string fname = path_from_pid(pid);
size_t fp = fname.find_last_of("/\\");
return fname.substr(0, fp + 1);
}
string name_from_pid(process_t pid) {
string fname = path_from_pid(pid);
size_t fp = fname.find_last_of("/\\");
return fname.substr(fp + 1);
}
procinfo/procinfo.h
#ifdef _WiN32
#include <windows.h>
typedef DWORD process_t;
#else
#include <sys/types.h>
typedef pid_t process_t;
#endif
#include <string>
/* windows-only helper function */
process_t ppid_from_pid(process_t pid);
/* get current process process id */
process_t pid_from_self();
/* get parent process process id */
process_t ppid_from_self();
/* std::string possible_result = "C:\\path\\to\\file.exe"; */
std::string path_from_pid(process_t pid);
/* std::string possible_result = "C:\\path\\to\\"; */
std::string dir_from_pid(process_t pid);
/* std::string possible_result = "file.exe"; */
std::string name_from_pid(process_t pid);
This allows getting the full path to the executable of pretty much any process id, except on Windows there are some process's with security attributes which simply will not allow it, so wysiwyg, this solution is not perfect.
To address what the question was asking more precisely, you may do this:
procinfo.cpp
#include "procinfo/procinfo.h"
#include <iostream>
using std::string;
using std::cout;
using std::endl;
int main() {
cout << dir_from_pid(pid_from_self()) << endl;
return 0;
}
Build the above file structure with this command:
procinfo.sh
cd "${0%/*}"
g++ procinfo.cpp procinfo/procinfo.cpp procinfo/win32/procinfo.cpp procinfo/macosx/procinfo.cpp procinfo/linux/procinfo.cpp procinfo/freebsd/procinfo.cpp -o procinfo.exe
For downloading a copy of the files listed above:
git clone git://github.com/time-killer-games/procinfo.git
For more cross-platform process-related goodness:
https://github.com/time-killer-games/enigma-dev
See the readme for a list of most of the functions included.
As others mentioned, argv[0] is quite a nice solution, provided that the platform actually passes the executable path, which is surely not less probable than the OS being Windows (where WinAPI can help find the executable path). If you want to strip the string to only include the path to the directory where the executable resides, then using that path to find other application files (like game assets if your program is a game) is perfectly fine, since opening files is relative to the working directory, or, if provided, the root.
The following works as a quick and dirty solution, but note that it is far from being foolproof:
#include <iostream>
using namespace std ;
int main( int argc, char** argv)
{
cout << argv[0] << endl ;
return 0;
}
In case you need to handle unicode paths for Windows:
#include <Windows.h>
#include <iostream>
int wmain(int argc, wchar_t * argv[])
{
HMODULE this_process_handle = GetModuleHandle(NULL);
wchar_t this_process_path[MAX_PATH];
GetModuleFileNameW(NULL, this_process_path, sizeof(this_process_path));
std::wcout << "Unicode path of this app: " << this_process_path << std::endl;
return 0;
}
There are several answers recommending using GetModuleFileName on Windows. These answers have some shortcomings like:
The code should work for both UNICODE and ANSI versions
The path can be longer than MAX_PATH
GetModuleFileName function can fail and return 0
GetModuleFileName can return a relative executable name instead of a full name
GetModuleFileName can return a short path like C:\GIT-RE~1\TEST_G~1\test.exe
Let me provide an improved version, which takes into account the abovementioned points:
#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>
// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
// First we need to get a length of the full name string
const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
if (full_name_length == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
if (res == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// The full name has been successfully written to the buffer.
return std::basic_string<TCHAR>(full_name_buffer.get());
}
// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
// First we need to get a length of the long name string
const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
if (long_name_length == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
if (res == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
// The long name has been successfully written to the buffer.
return std::basic_string<TCHAR>(long_name_buffer.get());
}
std::basic_string<TCHAR> get_current_executable_full_name()
{
DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that
// the path doesn't exceeds 260 characters
std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};
while (true) {
const auto bytes_written = GetModuleFileName(
NULL, path_buffer.get(), path_buffer_size);
const auto last_error = GetLastError();
if (bytes_written == 0) {
// GetModuleFileName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}
if (last_error == ERROR_INSUFFICIENT_BUFFER) {
// There is not enough space in our buffer to fit the path.
// We need to increase the buffer and try again.
path_buffer_size *= 2;
path_buffer.reset(new TCHAR[path_buffer_size]);
continue;
}
// GetModuleFileName has successfully written the executable name to the buffer.
// Now we need to convert it to a full long name
std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
return get_long_name(full_name.c_str());
}
}
// Example of how this function can be used
int main()
{
#ifdef UNICODE
// If you use UNICODE version of WinApi
std::wstring exe_file_full_name = get_current_executable_full_name();
std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
// If you use ANSI version of WinApi
std::string exe_file_full_name = get_current_executable_full_name();
std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}
For Windows, you have the problem of how to strip the executable from the result of GetModuleFileName(). The Windows API call PathRemoveFileSpec() that Nate used for that purpose in his answer changed between Windows 8 and its predecessors. So how to remain compatible with both and safe? Luckily, there's C++17 (or Boost, if you're using an older compiler). I do this:
#include <windows.h>
#include <string>
#include <filesystem>
namespace fs = std::experimental::filesystem;
// We could use fs::path as return type, but if you're not aware of
// std::experimental::filesystem, you probably handle filenames
// as strings anyway in the remainder of your code. I'm on Japanese
// Windows, so wide chars are a must.
std::wstring getDirectoryWithCurrentExecutable()
{
int size = 256;
std::vector<wchar_t> charBuffer;
// Let's be safe, and find the right buffer size programmatically.
do {
size *= 2;
charBuffer.resize(size);
// Resize until filename fits. GetModuleFileNameW returns the
// number of characters written to the buffer, so if the
// return value is smaller than the size of the buffer, it was
// large enough.
} while (GetModuleFileNameW(NULL, charBuffer.data(), size) == size);
// Typically: c:/program files (x86)/something/foo/bar/exe/files/win64/baz.exe
// (Note that windows supports forward and backward slashes as path
// separators, so you have to be careful when searching through a path
// manually.)
// Let's extract the interesting part:
fs::path path(charBuffer.data()); // Contains the full path including .exe
return path.remove_filename() // Extract the directory ...
.w_str(); // ... and convert to a string.
}
SDL2 (https://www.libsdl.org/) library has two functions implemented across a wide spectrum of platforms:
SDL_GetBasePath
SDL_GetPrefPath
So if you don't want to reinvent the wheel... sadly, it means including the entire library, although it's got a quite permissive license and one could also just copy the code. Besides, it provides a lot of other cross-platform functionality.
I didn't read if my solution is already posted but on linux and osx you can read the 0 argument in your main function like this:
int main(int argument_count, char **argument_list) {
std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
std::size_t pos = currentWorkingDirectoryPath.rfind("/"); // position of "live" in str
currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);
In the first item of argument_list the name of the executable is integrated but removed by the code above.
Here my simple solution that works in both Windows and Linux, based on this solution and modified with this answer:
#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux
string replace(const string& s, const string& from, const string& to) {
string r = s;
int p = 0;
while((p=(int)r.find(from, p))!=string::npos) {
r.replace(p, from.length(), to);
p += (int)to.length();
}
return r;
}
string get_exe_path() { // returns path where executable is located
string path = "";
#if defined(_WIN32)
wchar_t wc[260] = {0};
GetModuleFileNameW(NULL, wc, 260);
wstring ws(wc);
transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
path = replace(path, "\\", "/");
#elif defined(__linux__)
char c[260];
int length = (int)readlink("/proc/self/exe", c, 260);
path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
return path.substr(0, path.rfind('/')+1);
}
This was my solution in Windows. It is called like this:
std::wstring sResult = GetPathOfEXE(64);
Where 64 is the minimum size you think the path will be. GetPathOfEXE calls itself recursively, doubling the size of the buffer each time until it gets a big enough buffer to get the whole path without truncation.
std::wstring GetPathOfEXE(DWORD dwSize)
{
WCHAR* pwcharFileNamePath;
DWORD dwLastError;
HRESULT hrError;
std::wstring wsResult;
DWORD dwCount;
pwcharFileNamePath = new WCHAR[dwSize];
dwCount = GetModuleFileNameW(
NULL,
pwcharFileNamePath,
dwSize
);
dwLastError = GetLastError();
if (ERROR_SUCCESS == dwLastError)
{
hrError = PathCchRemoveFileSpec(
pwcharFileNamePath,
dwCount
);
if (S_OK == hrError)
{
wsResult = pwcharFileNamePath;
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
return wsResult;
}
else if(S_FALSE == hrError)
{
wsResult = pwcharFileNamePath;
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
//there was nothing to truncate off the end of the path
//returning something better than nothing in this case for the user
return wsResult;
}
else
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
std::ostringstream oss;
oss << "could not get file name and path of executing process. error truncating file name off path. last error : " << hrError;
throw std::runtime_error(oss.str().c_str());
}
}
else if (ERROR_INSUFFICIENT_BUFFER == dwLastError)
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
return GetPathOfEXE(
dwSize * 2
);
}
else
{
if (pwcharFileNamePath)
{
delete pwcharFileNamePath;
}
std::ostringstream oss;
oss << "could not get file name and path of executing process. last error : " << dwLastError;
throw std::runtime_error(oss.str().c_str());
}
}
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
in Unix(including Linux) try 'which', in Windows try 'where'.
#include <stdio.h>
#define _UNIX
int main(int argc, char** argv)
{
char cmd[128];
char buf[128];
FILE* fp = NULL;
#if defined(_UNIX)
sprintf(cmd, "which %s > my.path", argv[0]);
#else
sprintf(cmd, "where %s > my.path", argv[0]);
#endif
system(cmd);
fp = fopen("my.path", "r");
fgets(buf, sizeof(buf), fp);
fclose(fp);
printf("full path: %s\n", buf);
unlink("my.path");
return 0;
}
As of C++17:
Make sure you include std filesystem.
#include <filesystem>
and now you can do this.
std::filesystem::current_path().string()
boost filesystem became part of the standard lib.
if you can't find it try to look under:
std::experimental::filesystem
How do you iterate through every file/directory recursively in standard C++?
From C++17 onward, the <filesystem> header, and range-for, you can simply do this:
#include <filesystem>
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
std::cout << dirEntry << std::endl;
As of C++17, std::filesystem is part of the standard library and can be found in the <filesystem> header (no longer "experimental").
In standard C++, technically there is no way to do this since standard C++ has no conception of directories. If you want to expand your net a little bit, you might like to look at using Boost.FileSystem. This has been accepted for inclusion in TR2, so this gives you the best chance of keeping your implementation as close as possible to the standard.
An example, taken straight from the website:
bool find_file( const path & dir_path, // in this directory,
const std::string & file_name, // search for this name,
path & path_found ) // placing path here if found
{
if ( !exists( dir_path ) ) return false;
directory_iterator end_itr; // default construction yields past-the-end
for ( directory_iterator itr( dir_path );
itr != end_itr;
++itr )
{
if ( is_directory(itr->status()) )
{
if ( find_file( itr->path(), file_name, path_found ) ) return true;
}
else if ( itr->leaf() == file_name ) // see below
{
path_found = itr->path();
return true;
}
}
return false;
}
If using the Win32 API you can use the FindFirstFile and FindNextFile functions.
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
For recursive traversal of directories you must inspect each WIN32_FIND_DATA.dwFileAttributes to check if the FILE_ATTRIBUTE_DIRECTORY bit is set. If the bit is set then you can recursively call the function with that directory. Alternatively you can use a stack for providing the same effect of a recursive call but avoiding stack overflow for very long path trees.
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 &&
wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"\\" + ffd.cFileName);
}
else {
files.push_back(path + L"\\" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;
}
int main(int argc, char* argv[])
{
vector<wstring> files;
if (ListFiles(L"F:\\cvsrepos", L"*", files)) {
for (vector<wstring>::iterator it = files.begin();
it != files.end();
++it) {
wcout << it->c_str() << endl;
}
}
return 0;
}
You can make it even simpler with the new C++11 range based for and Boost:
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
struct recursive_directory_range
{
typedef recursive_directory_iterator iterator;
recursive_directory_range(path p) : p_(p) {}
iterator begin() { return recursive_directory_iterator(p_); }
iterator end() { return recursive_directory_iterator(); }
path p_;
};
for (auto it : recursive_directory_range(dir_path))
{
std::cout << it << std::endl;
}
A fast solution is using C's Dirent.h library.
Working code fragment from Wikipedia:
#include <stdio.h>
#include <dirent.h>
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir: Path does not exist or could not be read.");
return -1;
}
while ((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
You can use std::filesystem::recursive_directory_iterator. But beware, this includes symbolic (soft) links. If you want to avoid them you can use is_symlink. Example usage:
size_t directory_size(const std::filesystem::path& directory)
{
size_t size{ 0 };
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
{
if (entry.is_regular_file() && !entry.is_symlink())
{
size += entry.file_size();
}
}
return size;
}
In addition to the above mentioned boost::filesystem you may want to examine wxWidgets::wxDir and Qt::QDir.
Both wxWidgets and Qt are open source, cross platform C++ frameworks.
wxDir provides a flexible way to traverse files recursively using Traverse() or a simpler GetAllFiles() function. As well you can implement the traversal with GetFirst() and GetNext() functions (I assume that Traverse() and GetAllFiles() are wrappers that eventually use GetFirst() and GetNext() functions).
QDir provides access to directory structures and their contents. There are several ways to traverse directories with QDir. You can iterate over the directory contents (including sub-directories) with QDirIterator that was instantiated with QDirIterator::Subdirectories flag. Another way is to use QDir's GetEntryList() function and implement a recursive traversal.
Here is sample code (taken from here # Example 8-5) that shows how to iterate over all sub directories.
#include <qapplication.h>
#include <qdir.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QDir currentDir = QDir::current();
currentDir.setFilter( QDir::Dirs );
QStringList entries = currentDir.entryList();
for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry)
{
std::cout << *entry << std::endl;
}
return 0;
}
Boost::filesystem provides recursive_directory_iterator, which is quite convenient for this task:
#include "boost/filesystem.hpp"
#include <iostream>
using namespace boost::filesystem;
recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
std::cout << *it << std::endl;
}
You can use ftw(3) or nftw(3) to walk a filesystem hierarchy in C or C++ on POSIX systems.
We are in 2019. We have filesystem standard library in C++. The Filesystem library provides facilities for performing operations on file systems and their components, such as paths, regular files, and directories.
There is an important note on this link if you are considering portability issues. It says:
The filesystem library facilities may be unavailable if a hierarchical file system is not accessible to the implementation, or if it does not provide the necessary capabilities. Some features may not be available if they are not supported by the underlying file system (e.g. the FAT filesystem lacks symbolic links and forbids multiple hardlinks). In those cases, errors must be reported.
The filesystem library was originally developed as boost.filesystem, was published as the technical specification ISO/IEC TS 18822:2015, and finally merged to ISO C++ as of C++17. The boost implementation is currently available on more compilers and platforms than the C++17 library.
#adi-shavit has answered this question when it was part of std::experimental and he has updated this answer in 2017. I want to give more details about the library and show more detailed example.
std::filesystem::recursive_directory_iterator is an LegacyInputIterator that iterates over the directory_entry elements of a directory, and, recursively, over the entries of all subdirectories. The iteration order is unspecified, except that each directory entry is visited only once.
If you don't want to recursively iterate over the entries of subdirectories, then directory_iterator should be used.
Both iterators returns an object of directory_entry. directory_entry has various useful member functions like is_regular_file, is_directory, is_socket, is_symlink etc. The path() member function returns an object of std::filesystem::path and it can be used to get file extension, filename, root name.
Consider the example below. I have been using Ubuntu and compiled it over the terminal using
g++ example.cpp --std=c++17 -lstdc++fs -Wall
#include <iostream>
#include <string>
#include <filesystem>
void listFiles(std::string path)
{
for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
if (!dirEntry.is_regular_file()) {
std::cout << "Directory: " << dirEntry.path() << std::endl;
continue;
}
std::filesystem::path file = dirEntry.path();
std::cout << "Filename: " << file.filename() << " extension: " << file.extension() << std::endl;
}
}
int main()
{
listFiles("./");
return 0;
}
You would probably be best with either boost or c++14's experimental filesystem stuff. IF you are parsing an internal directory (ie. used for your program to store data after the program was closed), then make an index file that has an index of the file contents. By the way, you probably would need to use boost in the future, so if you don't have it installed, install it! Second of all, you could use a conditional compilation, e.g.:
#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif
The code for each case is taken from https://stackoverflow.com/a/67336/7077165
#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir: Path does not exist or could not be read.");
return -1;
}
while ((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 &&
wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"\\" + ffd.cFileName);
}
else {
files.push_back(path + L"\\" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;
}
#endif
//so on and so forth.
You don't. The C++ standard has no concept of directories. It is up to the implementation to turn a string into a file handle. The contents of that string and what it maps to is OS dependent. Keep in mind that C++ can be used to write that OS, so it gets used at a level where asking how to iterate through a directory is not yet defined (because you are writing the directory management code).
Look at your OS API documentation for how to do this. If you need to be portable, you will have to have a bunch of #ifdefs for various OSes.
You need to call OS-specific functions for filesystem traversal, like open() and readdir(). The C standard does not specify any filesystem-related functions.
You don't. Standard C++ doesn't expose to concept of a directory. Specifically it doesn't give any way to list all the files in a directory.
A horrible hack would be to use system() calls and to parse the results. The most reasonable solution would be to use some kind of cross-platform library such as Qt or even POSIX.
On C++17 you can by this way :
#include <filesystem>
#include <iostream>
#include <vector>
namespace fs = std::filesystem;
int main()
{
std::ios_base::sync_with_stdio(false);
for (const auto &entry : fs::recursive_directory_iterator(".")) {
if (entry.path().extension() == ".png") {
std::cout << entry.path().string() << std::endl;
}
}
return 0;
}
If you are on Windows, you can use the FindFirstFile together with FindNextFile API. You can use FindFileData.dwFileAttributes to check if a given path is a file or a directory. If it's a directory, you can recursively repeat the algorithm.
Here, I have put together some code that lists all the files on a Windows machine.
http://dreams-soft.com/projects/traverse-directory
File tree walk ftw is a recursive way to wall the whole directory tree in the path. More details are here.
NOTE : You can also use fts that can skip hidden files like . or .. or .bashrc
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
printf("0%3o\t%s\n", status->st_mode&0777, name);
}
if (type == FTW_D && strcmp(".", name) != 0)
{
printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
output looks like following:
0755 ./Shivaji/
0644 ./Shivaji/20200516_204454.png
0644 ./Shivaji/20200527_160408.png
0644 ./Shivaji/20200527_160352.png
0644 ./Shivaji/20200520_174754.png
0644 ./Shivaji/20200520_180103.png
0755 ./Saif/
0644 ./Saif/Snapchat-1751229005.jpg
0644 ./Saif/Snapchat-1356123194.jpg
0644 ./Saif/Snapchat-613911286.jpg
0644 ./Saif/Snapchat-107742096.jpg
0755 ./Milind/
0644 ./Milind/IMG_1828.JPG
0644 ./Milind/IMG_1839.JPG
0644 ./Milind/IMG_1825.JPG
0644 ./Milind/IMG_1831.JPG
0644 ./Milind/IMG_1840.JPG
Let us say if you want to match a filename (example: searching for all the *.jpg, *.jpeg, *.png files.) for a specific needs, use fnmatch.
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <fnmatch.h>
static const char *filters[] = {
"*.jpg", "*.jpeg", "*.png"
};
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
int i;
for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
/* if the filename matches the filter, */
if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
printf("0%3o\t%s\n", status->st_mode&0777, name);
break;
}
}
}
if (type == FTW_D && strcmp(".", name) != 0)
{
//printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
Answers of getting all file names recursively with C++11 for Windows and Linux(with experimental/filesystem):
For Windows:
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
void getFiles_w(string path, vector<string>& files) {
intptr_t hFile = 0;
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
do {
if ((fileinfo.attrib & _A_SUBDIR)) {
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("/").append(fileinfo.name), files);
}
else {
files.push_back(p.assign(path).append("/").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
}
}
For Linux:
#include <experimental/filesystem>
bool getFiles(std::experimental::filesystem::path path, vector<string>& filenames) {
namespace stdfs = std::experimental::filesystem;
// http://en.cppreference.com/w/cpp/experimental/fs/directory_iterator
const stdfs::directory_iterator end{} ;
for (stdfs::directory_iterator iter{path}; iter != end ; ++iter) {
// http://en.cppreference.com/w/cpp/experimental/fs/is_regular_file
if (!stdfs::is_regular_file(*iter)) { // comment out if all names (names of directories tc.) are required
if (getFiles(iter->path(), filenames))
return true;
}
else {
filenames.push_back(iter->path().string()) ;
cout << iter->path().string() << endl;
}
}
return false;
}
Just remember to link -lstdc++fs when you compile it with g++ in Linux.
Employee Visual C++ and WIN API:
bool Parser::queryDIR(string dir_name) {
vector<string> sameLayerFiles;
bool ret = false;
string dir = "";
//employee wide char
dir = dir_name + "\\*.*";;
//employee WIN File API
WIN32_FIND_DATA fd;
WIN32_FIND_DATA fd_dir;
HANDLE hFind = ::FindFirstFile(getWC(dir.c_str()), &fd);
HANDLE hFind_dir = ::FindFirstFile(getWC(dir.c_str()), &fd_dir);
string str_subdir;
string str_tmp;
//recursive call for diving into sub-directories
do {
if ((fd_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
//ignore trival file node
while(true) {
FindNextFile(hFind_dir, &fd_dir);
str_tmp = wc2str(fd_dir.cFileName);
if (str_tmp.compare(".") && str_tmp.compare("..")){
break;
}
}
if ((fd_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
str_subdir = wc2str(fd_dir.cFileName);
ret = queryDIR(dir_name + "\\" + str_subdir);
}
}
} while(::FindNextFile(hFind_dir, &fd_dir));
//iterate same layer files
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
str_tmp = wc2str(fd.cFileName);
string fname = dir_name + "\\" + str_tmp;
sameLayerFiles.push_back(fname);
}
} while(::FindNextFile(hFind, &fd));
for (std::vector<string>::iterator it=sameLayerFiles.begin(); it!=sameLayerFiles.end(); it++) {
std::cout << "iterated file:" << *it << "..." << std::endl;
//Doing something with every file here
}
return true;
}
Hope my code can help :)
And you can see more details and program screen-shots on My GitHub