Отримайте шлях до виконуваного файлу


115

Я знаю, що це питання було задано і раніше, але я все ще не бачив задовільної відповіді або остаточного "ні, цього не можна зробити", тому я знову запитую!

Все, що я хочу зробити, - це отримати шлях до запущеного в даний час виконуваного файлу, або як абсолютний шлях, або відносно того, звідки викликається виконуваний файл, незалежно від платформи. Я хоч boost :: filesystem :: begin_path був відповіддю на мої неприємності, але, здається, вирішує лише частину питання, незалежний від платформи - він все одно повертає шлях, з якого викликалася програма.

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



9
Зауважте, що неможливо довести відсутність відповіді, тому ви не можете отримати остаточний НІ. Я буду радий надати вам авторитетний НЕ :)
MSalters


" при завантаженні гра не знаходить файлів конфігурації тощо ", тому гра шукає файли конфігурації в поточному каталозі? Це погана ідея та потенційно вразлива безпека. Файли конфігурації повинні зберігатися у стандартному місці.
curiousguy

1
Тут я розмістив відповідь на відповідне запитання, яке також відповідає вашим, працюючи на платформах, використовуючи boost
jtbr

Відповіді:


86

Не існує перехресної платформи, яку я знаю.

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

Windows: GetModuleFileName


9
Незалежність платформи - це просто питання приховування залежності платформи. У цьому випадку використання методу заздалегідь визначених макросів ОС, детально описаних на predef.sourceforge.net/preos.html, для вибору методу є простим.
Кліффорд

4
Так це роблять усі, коли хочуть знайти шлях виконавчого файлу в C ++? Я сподівався на щось таке просте, як це вже буде реалізовано в такій бібліотеці, як boost.
Бен Хімерс

2
@curiousguy Я не впевнений, що тебе розумію; Я майже впевнений, що в цьому і полягає вся суть цього питання :)
Бен Хаммерс

6
@curiousguy: Ви б хотіли це зробити, якщо, наприклад, ваша програма може бути встановлена ​​в каталозі, обраному користувачем. Потрібно якось
greyfade

1
@Duck Ви б оновили свою відповідь посиланням на мою ліб? Мій коментар закопаний у списку.
Григорій Пакош

35

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

Далі - моє рішення. Я перевірив це на Windows, Mac OS X, Solaris, Free BSD та GNU / Linux.

Для нього потрібно збільшити 1,55,0 або вище. Він використовує бібліотеку Boost.Filesystem безпосередньо і Boost.Locale бібліотеку і Boost.System бібліотеку побічно.

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 / деталь / виконуваний_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 / деталі / 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_

У мене є повний проект, включаючи тестовий додаток та файли збирання CMake, доступні на SnKOpen - / cpp / Executable_path / trunk . Ця версія є більш повною, ніж версія, яку я надав тут. Він також підтримує більше платформ.

Я протестував додаток на всіх підтримуваних операційних системах у наступних чотирьох сценаріях.

  1. Відносний шлях, виконується в поточному каталозі: тобто ./executable_path_test
  2. Відносний шлях, виконується в іншому каталозі: тобто ./build/executable_path_test
  3. Повний шлях: тобто / some / dir / Executable_path_test
  4. Виконується у шляху, лише ім'я файлу: тобто Executable_path_test

У всіх чотирьох сценаріях виконується як виконуваний_пат, так і виконуваний_пат_падбек функції та повертають однакові результати.

Примітки

Це оновлена ​​відповідь на це питання. Я оновив відповідь, щоб врахувати коментарі та пропозиції користувачів. Я також додав посилання на проект у своєму сховищі SVN.


1
Це виглядає як дуже повноцінне рішення з розумними відкатами. +1! Хоча одне питання: чи має сенс замінити фіксовані буфери char [1024] чимось на зразок вектора <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()


здається, що стовпчик дає мені лише виконуваний мінус шлях та розширення для Windows, але це другорядний момент. Що я хотів би знати, як це працює навколо argv [0], можливо, неправильно? Це працює для мене тестування на Windows, але потім аргумент [0] фактично передається як абсолютний шлях виконуваного файлу, що робить роботу system_complete досить легким :)
Бен Гімерс

1
Ні - робочий каталог йому не потрібен. і NO аргумент не допомагає. Що робити, коли argv містить лише ім'я виконавця? Що робити, коли програму викликали через посилання?
Ічтіо

4
"// Без імені файлу" - ви хочете .parent_path(), ні .stem(), ні?
Клавдіу

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

3
Зауважте, що ви повинні використовувати WCHAR ownPth.., обернуті навколо, #ifdef UNICODEу тому випадку, якщо компілюється з підтримкою Unicode. Якщо ні, використовуйте наданий код.
Dr1Ku

1
просто для запису у мене просто смішний випадок, коли GetModuleDirectory повертає шлях із частинами ".." у ньому, як якщо б він брав рядок чистого сировини з командного рядка lol. насправді в цьому випадку візуальна студія запускає процес і .. є частиною шляху налагодження. щось на кшталт $ (projectDir) ../ some.exe Я використовував PathCanonicalize від Shwlib, але потрібно зв’язатися з цією lib. це може бути не бажаним.
v.oddou

1
Я також рекомендую використовувати TCHAR для ownPath замість char. Але приємна відповідь все одно.
anhoppe

Чи можливо навіть це не вдається? На HMODULE hModule = GetModuleHandle(NULL);
перший

1
Якщо першим параметром GetModuleFileName є NULL, він отримує шлях виконуваного файлу поточного процесу.
lsalamon

12

Для вікон:

GetModuleFileName - повертає шлях EXE + ім'я файлу exe

Щоб видалити ім'я файлу
PathRemoveFileSpec


1
Docs увагу на PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs

12

C ++ 17, windows, unicode, використовуючи файлову систему 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;
}

Підозрюєте, що це рішення має бути портативним, але не знаю, як unicode реалізований в інших ОС.

слабко_канонічний потрібен лише в тому випадку, якщо ви використовуєте в якості посилань верхньої папки Output Directory ('..') для спрощення шляху. Якщо ви не використовуєте - видаліть його.

Якщо ви працюєте з бібліотеки динамічних посилань (.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 () - тут кращий стандартний тест.
Метеорхед

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

Використовуючи аргументи [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;
}

ВЕДЕНО: Якщо '/' не існує, pos == - 1, тому результат правильний.


Що робити, якщо "/" немає у шляху? Немає перевірки цього випадку, і я вважаю, що це цілком ймовірно - Windows буде використовувати зворотні косої риси, і args[0]насправді це зовсім не може бути шлях.
Бен Хімерс

Якщо '/' не існує, rfind return -1, тому "path" = aux.substr (0,0) і "name" = aux.substr (0): результат правильний. Що стосується Windows, ви маєте рацію, "/" потрібно змінити на "\\", я зміню, щоб дозволити також і Windows. Я також перевірив назви файлів з '/', але це останнє кодифіковане і не створює проблем.
Адріан Мейр

1
Це більше частина про те, що args[0]не обов’язково бути виконавчим шляхом, який мене турбує. Дякуємо, що виправили свою відповідь для Windows :)
Ben Hymers

1
Якщо команда виконується без вказівки шляху (тобто вона знайдена, перебуваючи в каталозі, заданому в PATH env var), args [0] буде просто ім'ям виконуваного файлу без шляху.
Кевін

@Kevin: ви (та інші) маєте рацію, це просте рішення для невеликих інструментів, які працюють ~ 95% випадків. Для серйозного програмного забезпечення, можливо, краще конфігураційний файл та / або змінна середовище. Крім того, це потребує зазвичай не дуже хорошого (або навіть неправильного) дизайну.
Адріан Мейр


1

Далі працює як швидке та брудне рішення, але зауважте, що воно далеко не дурне:

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}

17
Я бачив в інших питаннях ТА, що це не завжди працює, і що argv [0] може містити абсолютний шлях до виконуваного файлу, лише ім'я файлу виконуваного файлу або будь-який інший сміття.
Бен Хімерс

7
Ніколи не слід довіряти argv [0], якщо вони намагаються відкрити "файли підтримки" тощо. Argv може змінюватися, і будь-який абонент, який злий, може змінити значення цього. Уникайте, якщо ви не використовуєте його для ведення журналів тощо, НЕ для створення контурів, які використовуються для відкриття файлів.
Qix - МОНІКА ПОМИЛИЛА

це не працює в Windows. argv [0] не буде мати повний шлях. Лише .exe файл. Будь ласка, не намагайтеся в баш-оболонці, спробуйте її у стандартній консолі та 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;
}

0

Для Windows у вас є проблема, як позбавити виконуваний файл в результаті GetModuleFileName(). Дзвінок API Windows, 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.
}

0

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


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

Тож якщо ви не хочете винаходити колесо ... на жаль, це означає включити всю бібліотеку, хоча у неї є досить дозвільна ліцензія, і ви також можете просто скопіювати код. Крім того, він пропонує багато інших крос-платформних функціоналів.


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


Зауважте, що це буде працювати лише в тому випадку, якщо / proc / self / exe існує та є доступним. Ви, ймовірно, повинні перевірити, чи це так.
Зрін

-1

Станом на C ++ 17:

Обов’язково включіть файлову систему std.

#include <filesystem>

і тепер ви можете це зробити.

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

файлова система boost стала частиною стандартної версії.

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

std::experimental::filesystem

10
Це не шлях двійкового, це поточний робочий каталог.
Zitrax

-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? Якби ви використовували std::vector, ваш код не проявляв би невизначеної поведінки.
Неочікувана

Крім того, GetModuleFileNameWне встановлюється останній код помилки у разі успіху. Цей код розбитий так багато способів. Не використовуйте, якщо вам трапляється натрапити на це.
IIнеочікуваний

-3
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));

2
Це лише Windows і використовує MFC, настільки далеко не кросплатформенний, вибачте!
Бен Хімерс

1
Це навіть не спосіб Windows зробити це. Перегляньте PathRemoveFileSpec()замість цього і пов'язані з ними функції.
Ремі Лебо

-4

в Unix (включаючи Linux) спробуйте "який", в Windows спробуйте "куди".

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

-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
Це повертає поточний робочий каталог, а не шлях до виконуваного файлу, який може бути не тим самим.
Дейв Дурбін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.