실행 파일의 경로 가져 오기


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.