실행 파일의 경로 가져 오기


115

이 질문이 이전에 요청 된 적이 있지만 여전히 만족스러운 답변이나 확실한 "아니요,이 작업을 수행 할 수 없습니다"를 보지 못 했으므로 다시 묻겠습니다!

내가 원하는 것은 플랫폼에 독립적 인 방식으로 현재 실행중인 실행 파일의 경로를 절대 경로로 또는 실행 파일이 호출 된 위치에 상대적으로 가져 오는 것입니다. 나는 boost :: filesystem :: initial_path가 내 문제에 대한 대답이지만 질문의 '플랫폼 독립적'부분 만 처리하는 것 같습니다. 여전히 응용 프로그램이 호출 된 경로를 반환합니다.

약간의 배경 지식을 위해 이것은 Ogre를 사용하는 게임입니다. Very Sleepy를 사용하여 프로파일 링하려고합니다. 이것은 자체 디렉터리에서 대상 실행 파일을 실행하므로로드시 게임은 구성 파일 등을 찾지 못하고 즉시 충돌합니다. . 구성 파일에 대한 절대 경로를 전달할 수 있기를 원합니다.이 경로는 항상 실행 파일과 함께있을 것입니다. Visual Studio에서 디버깅하는 경우에도 마찬가지입니다. 작업 디렉터리를 설정하지 않고도 $ (TargetPath)를 실행할 수 있기를 원합니다.



9
답이 없음을 증명하는 것은 불가능하므로 확실한 아니오를 얻을 수 없습니다 . 나는 당신에게 권위있는 NO를 제공하게되어 기쁩니다 :)
— MSalters


" 로드시 게임은 구성 파일 등을 찾지 못합니다. "그래서 게임은 현재 디렉토리에서 구성 파일을 검색합니까? 그것은 나쁜 생각이며 잠재적으로 보안 취약점입니다. 구성 파일은 표준 위치에 저장해야합니다.
— curiousguy

1
나는 게시 여기에 대한 답을 부스트 사용하여 플랫폼에 걸쳐 작업, 또한 당신에 응답 관련 질문
— jtbr

답변:


86

내가 아는 크로스 플랫폼 방식은 없습니다.

Linux의 경우 : readlink / proc / self / exe

Windows : GetModuleFileName


9
플랫폼 독립성은 단순히 플랫폼 종속성을 숨기는 문제입니다. 이 경우 predef.sourceforge.net/preos.html 에 자세히 설명 된 사전 정의 된 OS 매크로를 사용 하여 방법을 선택하는 것은 간단합니다.
— Clifford

4
그렇다면 모두가 C ++에서 실행 파일의 경로를 찾고 싶을 때마다하는 일입니까? 나는 이것이 부스트와 같은 라이브러리에서 이미 구현 될 것 같은 단순한 소리를 바라고 있었다.
— Ben Hymers

2
@curiousguy 내가 당신을 이해하는지 잘 모르겠습니다. 나는
— Ben Hymers

6
@curiousguy : 예를 들어 프로그램이 사용자가 선택한 디렉토리에 설치 될 수 있다면 그렇게하고 싶을 것입니다. 어떻게 든
— greyfade 2012-06-25

1
@Duck 내 lib에 대한 링크로 답변을 업데이트 하시겠습니까? 내 의견은 목록에 묻혀 있습니다.
— Gregory Pakosz 2015

35

부스트 :: DLL :: program_location의 기능은 내가 알고있는 것을 실행중인 실행 파일의 경로를 얻을 수있는 최고의 크로스 플랫폼 방법 중 하나입니다. DLL 라이브러리는 1.61.0 버전에서 Boost에 추가되었습니다.

다음은 내 솔루션입니다. Windows, Mac OS X, Solaris, Free BSD 및 GNU / Linux에서 테스트했습니다.

Boost 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 / 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_

SnKOpen-/ cpp / executable_path / trunk 에서 사용할 수있는 테스트 애플리케이션 및 CMake 빌드 파일을 포함하여 완전한 프로젝트가 있습니다 . 이 버전은 여기에서 제공 한 버전보다 더 완벽합니다. 또한 더 많은 플랫폼을 지원합니다.

다음 네 가지 시나리오에서 지원되는 모든 운영 체제에서 응용 프로그램을 테스트했습니다.

  1. 현재 디렉토리에서 실행 가능한 상대 경로 : ie ./executable_path_test
  2. 상대 경로, 다른 디렉토리에서 실행 가능 : 예 : ./build/executable_path_test
  3. 전체 경로 : ie / some / dir / executable_path_test
  4. 경로에서 실행 가능, 파일 이름 만 : ie executable_path_test

네 가지 시나리오 모두에서 executable_path 및 executable_path_fallback 함수가 모두 작동하고 동일한 결과를 반환합니다.

노트

이것은이 질문에 대한 업데이트 된 답변입니다. 사용자 의견과 제안을 고려하여 답변을 업데이트했습니다. 또한 SVN 저장소의 프로젝트에 대한 링크를 추가했습니다.


1
합리적인 폴 백이있는 매우 완벽한 솔루션처럼 보입니다. +1! 하지만 한 가지 질문이 있습니다. 고정 된 char [1024] 버퍼를 vector <char>와 같은 것으로 대체하여 경로가 초기 크기를 초과하면 크기를 조정할 수 있습니까?
— 다니엘 늑대

예. 그것은 훌륭한 제안입니다. 물론 오류 확인, 버퍼 크기 조정, 다시 시도와 같은 추가 변경이 필요합니다.
— Ben Key

1
대체가 올바르지 않다고 생각합니다. argv[0]실행 파일 이름 일 수도 있으며,이 경우 PATH* nix 시스템 에서 검색해야 합니다.
— Michał Górny

1
나는 이것을 사용해 보았다. 하지만 부스트가 필요 하죠? 나는 그것이 독립라고 생각
— manatttta

1
"boost :: dll :: program_location"에 저를
— Thomas

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의 작업이 매우 쉬워집니다. :)
— Ben Hymers

1
아니오-그는 작업 디렉토리가 필요하지 않습니다. 그리고 NO argv는 도움이되지 않습니다. argv 에 실행 파일 이름 만 포함되어 있으면 어떻게합니까? 프로그램이 심볼릭 링크를 통해 호출되었을 때 어떻게해야합니까?
— Ichthyo

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

3
유니 코드 지원으로 컴파일하는 경우 WCHAR ownPth..에는,를 사용해야합니다 #ifdef UNICODE. 그렇지 않은 경우 제공된 코드를 사용하십시오.
— Dr1Ku 2013

1
단지 기록을 위해 GetModuleDirectory가 ".."부분이있는 경로를 반환하는 재미있는 경우가 있습니다. 마치 명령 줄에서 순수한 원시 문자열을 가져 오는 것처럼 말입니다. 실제로이 경우 Visual Studio는 프로세스를 시작하고 ..는 디버깅 경로의 일부입니다. $ (projectDir) ../ some.exe와 같은 것 Shwlib에서 PathCanonicalize를 사용했지만이 lib에 링크해야합니다. 이것은 바람직하지 않을 수 있습니다.
— v.oddou 2014-06-29

1
또한 char 대신 ownPath에 TCHAR를 사용하는 것이 좋습니다. 그러나 어쨌든 좋은 대답.
— anhoppe

이것이 실패 할 수도 있습니까? 그것은 ... 한 눈에 아닌 것 같습니다HMODULE hModule = GetModuleHandle(NULL);
— kayleeFrye_onDeck

1
GetModuleFileName의 첫 번째 매개 변수가 NULL이면 현재 프로세스의 실행 파일 경로를 검색합니다.
— lsalamon

12

Windows의 경우 :

GetModuleFileName -exe 경로 + exe 파일 이름을 반환합니다.

파일 이름을 제거하려면
PathRemoveFileSpec


1
PathRemoveFileSpec에 대한 문서 노트 : This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
— javs

12

C ++ 17, Windows, 유니 코드, 파일 시스템 새 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;
}

이 솔루션은 이식 가능해야하지만 다른 OS에서 유니 코드가 어떻게 구현되는지 모릅니다.

weakly_canonical은 경로를 단순화하기 위해 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 ()는 여기서 더 나은 표준 테스트입니다.
— Meteorhead

8

QT는이를 QCoreApplication :: applicationDirPath () 로 OS 추상화와 함께 제공합니다.


이것으로 얻기 : QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. 그것을 해결하는 방법을 아십니까?
— GuySoft 2011 년

@GuySoft : 간단하게 QCoreApplication이와 같은 인스턴스를 생성 QApplication application(argc, argv);하십시오 (에서이 작업을 수행 하고을 main(argc, argv)수정하지 마십시오 argc/argv. QCoreApplication의 수명 동안 유효해야하므로 ( 문서 확인 )
— ted

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

수정 됨 : '/'가 없으면 pos ==-1이므로 결과가 정확합니다.


경로에 '/'가 없으면 어떻게합니까? 이 경우에 대한 검사가 없으며 Windows는 백 슬래시를 사용하며 args[0]실제로 경로가 아닐 수도 있습니다.
— Ben Hymers

'/'가 없으면 rfind는 -1을 반환하므로 "path"= aux.substr (0,0) 및 "name"= aux.substr (0) : 결과가 정확합니다. Windows와 관련하여 맞습니다. '/'는 '\\'로 변경해야합니다. 창도 허용하도록 변경하겠습니다. 나는 또한 '/'로 파일 이름을 테스트했지만 마지막은 성문화되어 문제를 일으키지 않습니다.
— Adrian Maire

1
args[0]필연적으로 나를 괴롭히는 실행 경로가 아닌 부분에 더 가깝습니다. :)하지만 Windows 용 답변을 고정 주셔서 감사합니다
— 벤 하이머을

1
명령이 경로를 제공하지 않고 실행되는 경우 (즉, PATH 환경 변수에 지정된 디렉토리에 있음으로써 발견됨) args [0]은 경로없이 실행 파일의 이름입니다.
— Kevin

@Kevin : 당신 (그리고 다른 사람들)이 옳습니다. 이것은 거의 95 %의 케이스에서 작동하는 간단한 솔루션입니다. 심각한 소프트웨어의 경우 구성 파일 및 / 또는 환경 변수가 더 좋습니다. 또한 이러한 요구는 일반적으로 좋지 않은 (또는 잘못된) 설계를 의미합니다.
— Adrian Maire 2015


1

다음은 빠르고 더러운 솔루션으로 작동하지만 절대적인 것은 아닙니다.

#include <iostream>

using namespace std ;

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

17
다른 SO 질문에서 이것이 항상 작동하는 것은 아니며 argv [0]에는 실행 파일의 절대 경로, 실행 파일의 파일 이름 또는 기타 쓰레기가 포함될 수 있다는 것을 보았습니다.
— Ben Hymers

7
'지원 파일'등을 열려고하는 경우 argv [0]을 신뢰해서는 안됩니다. Argv는 변경 될 수 있으며 악의적 인 호출자는이 값을 변경할 수 있습니다. 파일을 여는 데 사용되는 경로를 구성하는 것이 아니라 로깅 등에 사용하지 않는 한 피하십시오.
— Qix-MONICA는

이것은 Windows에서 작동하지 않습니다. argv [0]에는 전체 경로가 없습니다. .exe 파일 만. 제발, bash 쉘에서 시도하지 말고 표준 콘솔에서 시도하고 cout << argv [0] 재생산하십시오.
— Freddy Martinez Garcia

@FreddyMartinezGarcia 글쎄, 나는 그것을 Windows에서 테스트했을 것이므로 YMMV. 코드를 시작하는 데 사용 된 것입니다. CWD에서 실행 파일이 있으면 파일 이름 만 얻을 수 있습니다.
— 클리포드

0

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(). PathRemoveFileSpec()Nate가 대답에서 이러한 목적으로 사용한 Windows API 호출 은 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]플랫폼이 실제로 실행 경로를 전달한다는 점을 감안하면 상당히 좋은 솔루션입니다. 이는 OS가 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뿐만 아니라 모든 BSD에서 작동해야한다고 생각합니다. 솔라리스를 설치하는 일이 생기면 지원되는 목록에 해당 플랫폼을 추가 할 것입니다.

Windows에서 완전한 UTF-8 지원을 제공하므로 모든 사람이 그 정도까지는 신경 쓰지 않습니다.

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

이를 통해 거의 모든 프로세스 ID의 실행 파일에 대한 전체 경로를 얻을 수 있습니다. 단, Windows에는이를 허용하지 않는 보안 속성이있는 일부 프로세스가 있습니다. 따라서이 솔루션은 완벽하지 않습니다.

질문의 내용을보다 정확하게 해결하려면 다음을 수행하십시오.

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

위의 답변은 G ++ 9.3.0을 사용하여 Debian 10에서 테스트되었습니다.


이것은 / proc / self / exe가 있고 액세스 할 수있는 경우에만 작동합니다. 이것이 사실인지 확인해야 할 것입니다.
— Zrin

-1

C ++ 17 기준 :

std 파일 시스템을 포함했는지 확인하십시오.

#include <filesystem>

이제 이것을 할 수 있습니다.

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

부스트 파일 시스템은 표준 lib의 일부가되었습니다.

찾을 수없는 경우 아래를 살펴보십시오.

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코드가 정의되지 않은 동작을 나타내지 않았을 것입니다.
— IInspectable

또한 GetModuleFileNameW성공시 마지막 오류 코드를 설정하지 않습니다. 그 코드는 여러면에서 깨졌습니다. 이것을 우연히 발견하면 사용하지 마십시오.
— IInspectable 2011

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

2
그것은 Windows 전용이며 MFC를 사용하므로 크로스 플랫폼이 아닙니다. 죄송합니다!
— Ben Hymers 2014

1
이것은 Windows 방식도 아닙니다. PathRemoveFileSpec()대신 및 관련 기능을 살펴보십시오 .
— 레미 Lebeau

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

-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
이것은 동일한 것이 아닐 수도있는 실행 파일의 경로가 아니라 현재 작업 디렉토리를 반환합니다.
— Dave Durbin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.