Получить путь к исполняемому файлу

115

Я знаю, что этот вопрос задавался раньше, но я до сих пор не получил удовлетворительного ответа или однозначного «нет, это невозможно», поэтому я спрошу еще раз!

Все, что я хочу сделать, это получить путь к исполняемому файлу, запущенному в данный момент, либо в виде абсолютного пути, либо относительно того, откуда этот исполняемый файл вызывается, независимо от платформы. Я думал, что boost :: filesystem :: initial_path был ответом на мои проблемы, но, похоже, он обрабатывает только «платформенно-независимую» часть вопроса - он по-прежнему возвращает путь, из которого было вызвано приложение.

Для некоторой предыстории, это игра с использованием Ogre, которую я пытаюсь профилировать с помощью Very Sleepy, которая запускает целевой исполняемый файл из своего собственного каталога, поэтому, конечно, при загрузке игра не находит файлов конфигурации и т. Д. И сразу вылетает. . Я хочу иметь возможность передать ему абсолютный путь к файлам конфигурации, которые, как я знаю, всегда будут находиться рядом с исполняемым файлом. То же самое касается отладки в Visual Studio - я бы хотел иметь возможность запускать $ (TargetPath) без необходимости устанавливать рабочий каталог.

Бен Хаймерс
источник
3
stackoverflow.com/questions/1023306/… и другие
dmckee --- котенок экс-модератора
9
Обратите внимание, что невозможно доказать отсутствие ответа, поэтому вы не можете получить однозначное НЕТ. Я буду счастлив дать вам авторитетное НЕТ :)
MSalters
возможный дубликат того, как найти расположение исполняемого файла в C
ergosys
« при загрузке игра не находит файлов конфигурации и т. д. », поэтому игра ищет файлы конфигурации в текущем каталоге? Это плохая идея и потенциально уязвимость. Файлы конфигурации следует хранить в стандартном месте.
curiousguy
1
Я опубликовал здесь ответ на связанный с этим вопрос, который также отвечает на ваш, работая на разных платформах с использованием boost
jtbr

Ответы:

86

Я не знаю кроссплатформенного способа.

Для Linux: readlink / proc / self / exe

Windows: GetModuleFileName

Утка
источник
9
Независимость от платформы - это просто вопрос сокрытия зависимости от платформы. В этом случае использовать предопределенные макросы ОС, подробно описанные на страницеpref.sourceforge.net/preos.html, для выбора метода очень просто.
Клиффорд,
4
Так что же все делают, когда хотят найти путь к исполняемому файлу в C ++? Я надеялся, что что-то столь же простое, поскольку это уже будет реализовано в такой библиотеке, как boost.
Бен Хаймерс,
2
@curiousguy Я не уверен, что понимаю вас; Я почти уверен, что в этом весь смысл этого вопроса :)
Бен Хаймерс
6
@curiousguy: Вы захотите сделать это, если, например, ваша программа может быть установлена ​​в каталог по выбору пользователя. Вам нужно каким-то образом
greyfade
1
@ Дак, не могли бы вы обновить свой ответ ссылкой на мою библиотеку? Мой комментарий похоронен в списке.
Грегори Пакош
35

Функция boost :: dll :: program_location - один из лучших кроссплатформенных методов получения пути к исполняемому файлу, о котором я знаю. Библиотека DLL была добавлена ​​в Boost в версии 1.61.0.

Вот мое решение. Я тестировал его в Windows, Mac OS X, Solaris, Free BSD и GNU / Linux.

Требуется Boost 1.55.0 или выше. Он использует библиотеку Boost.Filesystem непосредственно и Boost.Locale библиотеку и Boost.System библиотеку косвенно.

SRC / исполняемый_путь.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 / деталь / исполняемый_путь_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;
}

}
}

включить / boost / исполняемый_путь.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_

включить / boost / detail / исполняемый_путь_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_

У меня есть полный проект, включая тестовое приложение и файлы сборки CMake, доступные в SnKOpen - / cpp / executable_path / trunk . Эта версия более полная, чем версия, которую я предоставил здесь. Он также поддерживает больше платформ.

Я протестировал приложение на всех поддерживаемых операционных системах в следующих четырех сценариях.

  1. Относительный путь исполняемого файла в текущем каталоге: т.е. ./executable_path_test
  2. Относительный путь, исполняемый файл в другом каталоге: т.е. ./build/executable_path_test
  3. Полный путь: т.е. / some / dir / executable_path_test
  4. Путь к исполняемому файлу, только имя файла: например, исполняемый_путь_тест

Во всех четырех сценариях обе функции executable_path и executable_path_fallback работают и возвращают одинаковые результаты.

Ноты

Это обновленный ответ на этот вопрос. Я обновил ответ, чтобы учесть комментарии и предложения пользователей. Я также добавил ссылку на проект в моем репозитории SVN.

Бен Ки
источник
1
Это похоже на очень полное решение с разумными откатами. +1! Однако есть вопрос: имеет ли смысл заменить фиксированные буферы char [1024] чем-то вроде vector <char>, размер которого можно изменять, если путь превышает исходный размер?
Дэниел Вольф
Да. Это отличное предложение. Конечно, потребуется внести некоторые дополнительные изменения, такие как проверка на наличие ошибок, изменение размера буфера и повторная попытка.
Бен Ки
1
Я думаю, что запасной вариант неправильный. argv[0]также может быть просто исполняемым именем, и в этом случае его необходимо будет искать в PATHсистемах * nix.
Michał Górny
1
Я пробовал использовать это. но ему нужен импульс, верно? Я думал, что это автономно
manatttta
1
У вас был я в «boost :: dll :: program_location»
Томас
31

Этот способ использует boost + argv. Вы упомянули, что это не может быть кросс-платформенный, потому что он может включать или не включать имя исполняемого файла. Следующий код должен обойти это.

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

Следующий код получает текущий рабочий каталог, который может делать то, что вам нужно.

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

Примечание. Только что понял, что basename() устарел, поэтому пришлось переключиться на.stem()

Рю
источник
Похоже, что stem дает мне только исполняемый файл без пути и расширения в Windows, но это второстепенный момент. Я хотел бы знать, как это работает, когда argv [0], возможно, неверен? У меня работает тестирование в Windows, но тогда argv [0] фактически передается как абсолютный путь к исполняемому файлу, что значительно упрощает работу system_complete :)
Бен Хаймерс,
1
Нет - ему не нужен рабочий каталог. и НЕТ argv не помогает. Что делать, если argv содержит только имя исполняемого файла? Что делать, если программа была вызвана по символической ссылке?
Ichthyo 01
4
«// Без имени файла» - хочешь .parent_path(), нет .stem(), нет?
Claudiu
2
Похоже, это не работает на моей платформе (macOS El Capitan). Вместо этого я получаю текущий рабочий каталог. Также, как я @Claudiuсказал, думаю, так и должно быть .parent_path().
samvv
20

Я не уверен насчет Linux, но попробуйте это для 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;
     }
}
SturmCoder
источник
3
Обратите внимание, что нужно использовать WCHAR ownPth.., обернутый вокруг a, #ifdef UNICODEесли он компилируется с поддержкой Unicode. Если нет, используйте предоставленный код.
Dr1Ku
1
просто для записи, у меня просто забавный случай, когда GetModuleDirectory возвращает путь с частями ".." в нем, как если бы он брал строку в чистом виде из командной строки lol. фактически в этом случае Visual Studio запускает процесс, а .. является частью пути отладки. что-то вроде $ (projectDir) ../ some.exe Я использовал PathCanonicalize из Shwlib, но нужно связать его с этой библиотекой. это может быть нежелательно.
v.oddou
1
Я также рекомендую использовать TCHAR для ownPath вместо char. Но все равно хороший ответ.
Анхоп,
Возможно ли, что это вообще не удастся? На первый взгляд это кажется маловероятным ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck 07
1
Если первый параметр для GetModuleFileName равен NULL, он получает путь к исполняемому файлу текущего процесса.
Исаламон,
13

C ++ 17, windows, unicode, с использованием нового 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;
}

Подозреваю, что это решение должно быть переносимым, но не знаю, как реализован Unicode в других ОС.

weakly_canonical требуется только в том случае, если вы используете в качестве ссылок на верхнюю папку выходного каталога ('..') для упрощения пути. Если не пользуетесь - удалите.

Если вы работаете из библиотеки динамической компоновки (.dll /.so), тогда у вас может не быть argv, тогда вы можете рассмотреть следующее решение:

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
}
ТармоПикаро
источник
Защита внутри заголовка не является надлежащей проверкой наличия файловой системы. cppreference показывает, что значение макроса тестирования функций определяется в самом заголовке файловой системы, поэтому он не работает для тестирования перед включением. __has_include () - лучший стандартный тест здесь.
Meteorhead
12

Для окон:

GetModuleFileName - возвращает путь exe + имя файла exe

Чтобы удалить имя файла
PathRemoveFileSpec

Сивабалан
источник
1
Docs внимание на PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs
8

QT предоставляет это с абстракцией ОС как QCoreApplication :: applicationDirPath ()

Тед
источник
Как с этим: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Есть идеи, как это решить?
GuySoft
@GuySoft: Просто создать экземпляр , QCoreApplicationкак так QApplication application(argc, argv);(сделать это в main(argc, argv), и быть уверенным , что вы не изменяете argc/argv, так как они должны оставаться в силе в течение всего срока службы QCoreApplication (проверьте документацию )
Тед
5

Это специфичный для Windows способ, но это как минимум половина вашего ответа.

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

Я бы предложил использовать определение платформы в качестве директив препроцессора, чтобы изменить реализацию функции-оболочки, которая вызывает GetThisPathдля каждой платформы.

Нейт
источник
3

Используя args [0] и ищите '/' (или '\\'):

#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: если '/' не существует, pos == - 1, поэтому результат правильный.

Адриан Мэр
источник
Что делать, если в пути нет символа «/»? В этом случае нет проверки, и я считаю, что это вполне вероятно - Windows будет использовать обратную косую черту и args[0]может вообще не быть путем.
Ben Hymers
Если '/' не существует, rfind вернет -1, поэтому «path» = aux.substr (0,0) и «name» = aux.substr (0): результат правильный. Что касается Windows, вы правы, '/' необходимо заменить на '\\', я также изменю, чтобы разрешить окна. Я также тестировал имена файлов с '/', но последний кодифицирован и не создает проблем.
Адриан Мэр
1
Меня больше args[0]беспокоит то, что это не обязательно путь к исполняемому файлу. Тем не менее, спасибо за исправление вашего ответа для Windows :)
Бен Хаймерс,
1
Если команда запускается без указания пути (т.е. она находится в каталоге, указанном в переменной PATH env), args [0] будет просто именем исполняемого файла без пути.
Кевин
@Kevin: вы (и другие) правы, это простое решение для небольших инструментов, которое работает ~ 95% случаев. Для серьезного программного обеспечения, вероятно, лучше использовать файл конфигурации и / или переменную среды. Кроме того, это обычно подразумевает не очень удачный (или даже неправильный) дизайн.
Адриан Мэйр,
1

Следующее работает как быстрое и грязное решение, но учтите, что оно далеко не надежно:

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}
Клиффорд
источник
17
По другим вопросам SO я видел, что это не всегда работает, и что argv [0] может содержать абсолютный путь к исполняемому файлу, только имя файла исполняемого файла или любой другой мусор.
Бен Хаймерс,
7
Никогда не следует доверять argv [0], если они пытаются открыть «файлы поддержки» или тому подобное. Argv может быть изменен, и любой злой вызывающий объект может изменить его значение. Избегайте, если вы не используете его для ведения журнала и т.д., НЕ для создания путей, используемых для открытия файлов.
Qix - МОНИКА БЫЛА ОФИЦИАЛЬНЫМ ОБРАЩЕНИЕМ
это не работает в Windows. argv [0] не будет иметь полного пути. Только файл .exe. Пожалуйста, не пытайтесь использовать оболочку bash, попробуйте ее в стандартной консоли и cout << argv [0] для воспроизведения.
Фредди Мартинес Гарсия,
@FreddyMartinezGarcia Ну, я бы протестировал его в Windows, так что YMMV. Это то, что использовалось для запуска кода. Если вы используете исполняемый файл в CWD, вы обязательно получите только имя файла.
Клиффорд
0

Если вам нужно обрабатывать пути Unicode для 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;
}
kayleeFrye_onDeck
источник
0

Для Windows у вас есть проблема, как удалить исполняемый файл из результата GetModuleFileName(). Вызов Windows API, PathRemoveFileSpec()который Нейт использовал для этой цели в своем ответе, изменился между Windows 8 и ее предшественниками. Итак, как оставаться совместимым и безопасным? К счастью, есть C ++ 17 (или Boost, если вы используете более старый компилятор). Я сделаю это:

#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.
}
tobi_s
источник
0

Как уже упоминалось, argv[0]это довольно хорошее решение при условии, что платформа фактически передает исполняемый путь, что, безусловно, не менее вероятно, чем ОС, являющаяся Windows (где WinAPI может помочь найти исполняемый путь). Если вы хотите разделить строку, чтобы включить только путь к каталогу, в котором находится исполняемый файл, то использование этого пути для поиска других файлов приложения (например, игровых ресурсов, если ваша программа является игрой) совершенно нормально, поскольку открытие файлов происходит относительно рабочий каталог или, если он есть, корень.

Kotauskas
источник
0

Вот что у меня получилось

Заголовочный файл выглядит так:

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

}

Реализация


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

}
Атула
источник
0

Библиотека SDL2 ( https://www.libsdl.org/ ) имеет две функции, реализованные на широком спектре платформ:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Так что, если вы не хотите изобретать велосипед ... к сожалению, это означает включение всей библиотеки, хотя у нее есть довольно разрешительная лицензия, и можно также просто скопировать код. Кроме того, он предоставляет множество других кроссплатформенных функций.

user1050755
источник
0

Вероятно, это наиболее естественный способ сделать это, охватывающий большинство основных настольных платформ. Я не уверен, но я считаю, что это должно работать со всеми BSD, а не только с FreeBSD, если вы измените проверку макроса платформы, чтобы охватить их все. Если мне удастся установить Solaris, я обязательно добавлю эту платформу в список поддерживаемых.

Имеет полную поддержку UTF-8 в Windows, что не всем важно, чтобы зайти так далеко.

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

Это позволяет получить полный путь к исполняемому файлу практически любого идентификатора процесса, за исключением того, что в Windows есть некоторые процессы с атрибутами безопасности, которые просто не позволяют этого, поэтому wysiwyg, это решение не идеально.

Чтобы более точно ответить на вопрос, вы можете сделать это:

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

Создайте указанную выше файловую структуру с помощью этой команды:

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

Для загрузки копии файлов, перечисленных выше:

git clone git://github.com/time-killer-games/procinfo.git

Для получения дополнительной информации о кросс-платформенных процессах:

https://github.com/time-killer-games/enigma-dev

Ознакомьтесь с файлом readme для получения списка большинства включенных функций.

Сэмюэл Джозеф Венейбл
источник
0

При использовании C ++ 17 можно сделать следующее, чтобы получить путь к исполняемому файлу.

#include <filesystem>

std::filesystem::path getExecutablePath()
{
    return std::filesystem::canonical("/proc/self/exe");
}

Приведенный выше ответ был протестирован на Debian 10 с использованием G ++ 9.3.0.

Scillman
источник
Обратите внимание, что это будет работать, только если / proc / self / exe существует и доступен. Вам, вероятно, следует проверить, так ли это.
Зрин
-2

Это было моим решением в Windows. Он называется так:

std::wstring sResult = GetPathOfEXE(64);

Где 64 - это минимальный размер, по вашему мнению, будет путь. GetPathOfEXE вызывает себя рекурсивно, каждый раз удваивая размер буфера, пока он не получит достаточно большой буфер, чтобы получить весь путь без усечения.

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());
    }
}
Тимоти Джон Лэрд
источник
В чем причина использования newи (неправильного) delete? Если бы вы использовали a std::vector, ваш код не показал бы неопределенного поведения.
IInspectable
Кроме того, GetModuleFileNameWв случае успеха не устанавливает последний код ошибки. Этот код сломан по-разному. Не используйте, если случайно наткнетесь на это.
IInspectable
-2

Начиная с C ++ 17:

Убедитесь, что вы включили файловую систему std.

#include <filesystem>

и теперь ты можешь это сделать.

std::filesystem::current_path().string()

файловая система boost стала частью стандартной библиотеки lib.

если вы не можете найти его, попробуйте заглянуть в:

std::experimental::filesystem
Tomer Zeitune
источник
10
Это не путь к двоичному файлу, это текущий рабочий каталог.
Zitrax
-3
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
Vishwa
источник
2
Это только для Windows и использует MFC, поэтому извините, это далеко не кроссплатформенность!
Бен Хаймерс,
1
Это даже не способ сделать это в Windows. PathRemoveFileSpec()Вместо этого взгляните на и связанные функции.
Реми Лебо
-4

в Unix (включая Linux) попробуйте "which", в Windows попробуйте "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;
}
chmn
источник
-4

Этот метод работает как для Windows, так и для Linux:

#include <stdio.h>
#include <string>
#ifdef _WIN32
#include <direct.h>
#define GetCurrentDir _getcwd
#elif __linux__
#include <unistd.h>
#define GetCurrentDir getcwd
#endif

std::string GetCurrentWorkingDir() 
{
    char buff[FILENAME_MAX];
    GetCurrentDir(buff, FILENAME_MAX);
    std::string current_working_dir(buff);
    return current_working_dir;
}
Джеймс Март
источник
2
Это возвращает текущий рабочий каталог, а не путь к исполняемому файлу, который может быть другим.
Дэйв Дурбин