Nhận đường dẫn có thể thực thi


114

Tôi biết câu hỏi này đã được đặt ra từ trước nhưng vẫn chưa thấy câu trả lời thỏa đáng, hoặc câu trả lời dứt khoát "không, việc này không thể làm được" nên tôi sẽ hỏi lại!

Tất cả những gì tôi muốn làm là lấy đường dẫn đến tệp thực thi hiện đang chạy, dưới dạng đường dẫn tuyệt đối hoặc liên quan đến nơi tệp thực thi được gọi từ đó, theo kiểu độc lập với nền tảng. Mặc dù boost :: filesystem :: initial_path là câu trả lời cho những rắc rối của tôi nhưng điều đó dường như chỉ xử lý phần 'độc lập với nền tảng' của câu hỏi - nó vẫn trả về đường dẫn mà từ đó ứng dụng được gọi.

Đối với một chút thông tin cơ bản, đây là một trò chơi sử dụng Ogre, mà tôi đang cố gắng lập hồ sơ bằng Very Sleepy, chạy tệp thực thi mục tiêu từ thư mục của chính nó, vì vậy tất nhiên khi tải trò chơi không tìm thấy tệp cấu hình nào, v.v. và ngay lập tức bị lỗi . Tôi muốn có thể chuyển nó một đường dẫn tuyệt đối đến các tệp cấu hình, mà tôi biết sẽ luôn tồn tại cùng với tệp thực thi. Tương tự với gỡ lỗi trong Visual Studio - tôi muốn có thể chạy $ (TargetPath) mà không cần phải đặt thư mục làm việc.



9
Lưu ý rằng không thể chứng minh sự vắng mặt của câu trả lời, do đó bạn không thể nhận được câu trả lời KHÔNG dứt khoát . Tôi sẽ rất vui khi cung cấp cho bạn một KHÔNG có thẩm quyền :)
MSalters


" khi tải trò chơi không tìm thấy tệp cấu hình nào, v.v. " vì vậy trò chơi tìm kiếm tệp cấu hình trên thư mục hiện tại? Đó là một ý tưởng tồi và có khả năng là một lỗ hổng bảo mật. Các tệp cấu hình phải được lưu trữ ở một vị trí tiêu chuẩn.
tò mò

1
Tôi đăng một câu trả lời ở đây cho một câu hỏi liên quan mà cũng trả lời của bạn, làm việc trên nền tảng sử dụng tăng
jtbr

Câu trả lời:


86

Không có cách nào đa nền tảng mà tôi biết.

Đối với Linux: readlink / proc / self / exe

Windows: GetModuleFileName


9
Nền tảng độc lập chỉ đơn giản là vấn đề che giấu sự phụ thuộc của nền tảng. Trong trường hợp này, việc sử dụng macro hệ điều hành được xác định trước được nêu chi tiết tại predef.sourceforge.net/preos.html để chọn phương pháp rất đơn giản.
Clifford

4
Vì vậy, đây có phải là điều mà mọi người làm bất cứ khi nào họ muốn tìm đường dẫn của tệp thực thi trong C ++? Tôi đã hy vọng một cái gì đó nghe có vẻ đơn giản như điều này đã được triển khai trong một thư viện như boost.
Ben Hymers

2
@curiousguy Tôi không chắc mình hiểu bạn; Tôi khá chắc đó là toàn bộ điểm của câu hỏi này :)
Ben Hymers

6
@curiousguy: Ví dụ: bạn sẽ muốn làm điều đó nếu chương trình của bạn có thể được cài đặt trong một thư mục do người dùng lựa chọn. Bạn cần có thể tìm thấy tệp thực thi của mình và các tệp hỗ trợ của nó bằng cách nào đó.
greyfade

1
@Duck bạn có cập nhật câu trả lời của mình bằng liên kết đến lib của tôi không? Bình luận của tôi được chôn xuống danh sách.
Gregory Pakosz

35

Sự gia tăng :: dll :: program_location là một trong những phương pháp đa nền tảng tốt nhất để lấy đường dẫn của tệp thực thi đang chạy mà tôi biết. Thư viện DLL đã được thêm vào Boost trong phiên bản 1.61.0.

Sau đây là giải pháp của tôi. Tôi đã thử nghiệm nó trên Windows, Mac OS X, Solaris, BSD miễn phí và GNU / Linux.

Nó yêu cầu Boost 1.55.0 trở lên. Nó sử dụng các thư viện Boost.Filesystem trực tiếp và Boost.Locale thư viện và Boost.System thư viện gián tiếp.

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

}
}

bao gồm / boost / execute_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_

bao gồm / 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_

Tôi có một dự án hoàn chỉnh, bao gồm một ứng dụng thử nghiệm và các tệp bản dựng CMake có sẵn tại SnKOpen - / cpp / execute_path / trunk . Phiên bản này hoàn thiện hơn phiên bản tôi cung cấp ở đây. Nó cũng hỗ trợ nhiều nền tảng hơn.

Tôi đã thử nghiệm ứng dụng trên tất cả các hệ điều hành được hỗ trợ trong bốn trường hợp sau.

  1. Đường dẫn tương đối, có thể thực thi trong thư mục hiện tại: tức là ./executable_path_test
  2. Đường dẫn tương đối, có thể thực thi trong một thư mục khác: tức là ./build/executable_path_test
  3. Đường dẫn đầy đủ: tức là / some / dir / executable_path_test
  4. Chỉ thực thi trong đường dẫn, tên tệp: tức là tệp thực thi_path_test

Trong tất cả bốn trường hợp, cả hai hàm execute_path và thực thi_path_fallback đều hoạt động và trả về kết quả giống nhau.

Ghi chú

Đây là câu trả lời cập nhật cho câu hỏi này. Tôi đã cập nhật câu trả lời để xem xét các nhận xét và đề xuất của người dùng. Tôi cũng đã thêm một liên kết đến một dự án trong Kho lưu trữ SVN của mình.


1
Đó có vẻ như là một giải pháp rất hoàn chỉnh với các khoản dự phòng hợp lý. +1! Tuy nhiên, một câu hỏi đặt ra: Liệu có hợp lý nếu thay thế các bộ đệm char [1024] cố định bằng một thứ gì đó như vectơ <char> có thể được thay đổi kích thước nếu đường dẫn vượt quá kích thước ban đầu không?
Daniel Wolf

Đúng. Đó là một gợi ý tuyệt vời. Tất nhiên một số thay đổi bổ sung sẽ cần được thực hiện như kiểm tra lỗi, thay đổi kích thước bộ đệm và thử lại.
Ben Key

1
Tôi nghĩ rằng dự phòng là không chính xác. argv[0]cũng có thể chỉ là tên thực thi, trong trường hợp đó, cần phải tìm kiếm nó trong PATHhệ thống * nix.
Michał Górny

1
Tôi đã thử sử dụng cái này. nhưng nó cần tăng cường, đúng không? Tôi nghĩ đó là độc lập
manatttta

1
Bạn đã có tôi tại "boost :: dll :: program_location"
Thomas

31

Cách này sử dụng boost + argv. Bạn đã đề cập đây có thể không phải là nền tảng chéo vì nó có thể có hoặc không bao gồm tên thực thi. Mã sau sẽ hoạt động xung quanh điều đó.

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

Đoạn mã sau lấy thư mục làm việc hiện tại có thể làm những gì bạn cần

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

Lưu ý Chỉ cần nhận ra rằng basename() không được dùng nữa nên phải chuyển sang.stem()


cuống dường như chỉ cung cấp cho tôi tệp thực thi trừ đường dẫn và tiện ích mở rộng trên Windows, nhưng đó là một điểm nhỏ. Điều tôi muốn biết là cách điều này hoạt động xung quanh argv [0] có thể không chính xác? Nó làm việc cho tôi thử nghiệm trên Windows, nhưng sau đó argv [0] là thực sự được thông qua tại như đường dẫn tuyệt đối của file thực thi, mà làm cho công việc của system_complete khá dễ dàng :)
Bến Hymers

1
Không - anh ta không cần danh bạ làm việc. và NO argv không giúp được gì. Bạn làm gì khi argv chỉ chứa tên thực thi? Phải làm gì, khi chương trình được gọi qua một liên kết tượng trưng?
Ichthyo

4
"// Không có tên tệp" - bạn muốn .parent_path(), không .stem(), không?
Claudiu

2
Điều này dường như không hoạt động trên nền tảng của tôi (macOS El Capitan). Thay vào đó, tôi nhận được thư mục làm việc hiện tại. Ngoài ra, như đã @Claudiunói, tôi nghĩ nó nên như vậy .parent_path().
samvv

20

Tôi không chắc về Linux, nhưng hãy thử điều này cho 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
Lưu ý rằng người ta phải sử dụng WCHAR ownPth.., được bao bọc xung quanh a #ifdef UNICODEtrong trường hợp người đó biên dịch với hỗ trợ Unicode. Nếu không, hãy sử dụng mã được cung cấp.
Dr1Ku

1
đối với bản ghi, tôi chỉ gặp một trường hợp buồn cười trong đó GetModuleDirectory trả về một đường dẫn với các phần ".." trong đó, giống như thể nó đang lấy chuỗi nguyên thuần từ dòng lệnh lol. thực sự trong trường hợp này visual studio đang khởi chạy quy trình và .. là một phần của đường dẫn gỡ lỗi. một cái gì đó như $ (projectDir) ../ some.exe Tôi đã sử dụng PathCanonicalize từ Shwlib nhưng một người cần liên kết với lib này. điều này có thể không được mong muốn.
v.oddou

1
Tôi cũng muốn sử dụng TCHAR cho ownPath thay vì char. Nhưng dù sao thì câu trả lời cũng hay.
anhoppe

Nó thậm chí có thể cho điều này để thất bại? Nó có vẻ như không trong nháy mắt ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck

1
Nếu tham số đầu tiên cho GetModuleFileName là NULL, nó sẽ truy xuất đường dẫn của tệp thực thi của quy trình hiện tại.
lsalamon

12

Cho cửa sổ:

GetModuleFileName - trả về đường dẫn exe + tên tệp exe

Để xóa tên tệp
PathRemoveFileSpec


1
Documents lưu ý cho PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs

12

C ++ 17, windows, unicode, sử dụng api mới của hệ thống tập tin:

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

Tôi nghi ngờ rằng giải pháp này có thể di động, nhưng không biết unicode được triển khai như thế nào trên các hệ điều hành khác.

Yếu tố_canonical chỉ cần thiết nếu bạn sử dụng làm tham chiếu thư mục trên của Thư mục đầu ra ('..') để đơn giản hóa đường dẫn. Nếu bạn không sử dụng nó - hãy loại bỏ nó.

Nếu bạn đang hoạt động từ thư viện liên kết động (.dll /.so), thì bạn có thể không có argv, khi đó bạn có thể xem xét giải pháp sau:

ứng dụng.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
}

Các bảo vệ bên trong tiêu đề không phải là thử nghiệm thích hợp cho sự hiện diện của hệ thống tệp. cppreference cho thấy giá trị của macro kiểm tra tính năng được xác định trong chính tiêu đề hệ thống tệp, do đó nó không hoạt động để kiểm tra trước khi đưa vào. __has_include () là một thử nghiệm tiêu chuẩn tốt hơn ở đây.
Meteorhead

8

QT cung cấp điều này với sự trừu tượng hóa hệ điều hành là QCoreApplication :: applicationDirPath ()


Bắt với điều này: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Bất kỳ ý tưởng làm thế nào để giải quyết điều đó?
GuySoft

@GuySoft: Đơn giản chỉ cần tạo một phiên bản QCoreApplicationtương tự QApplication application(argc, argv);(làm điều này trong của bạn main(argc, argv)và đảm bảo rằng bạn không sửa đổi argc/argv, vì chúng cần phải duy trì hiệu lực trong suốt thời gian tồn tại của QCoreApplication (kiểm tra tài liệu )
ted vào

5

Đây là một cách dành riêng cho Windows, nhưng nó ít nhất là một nửa câu trả lời của bạn.

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

Tôi sẽ đề xuất sử dụng tính năng phát hiện nền tảng làm chỉ thị tiền xử lý để thay đổi việc triển khai hàm trình bao bọc gọi GetThisPathcho từng nền tảng.


3

Sử dụng args [0] và tìm kiếm '/' (hoặc '\\'):

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

CHỈNH SỬA: Nếu '/' không tồn tại, pos == - 1 để kết quả là đúng.


Điều gì xảy ra nếu '/' không có trong đường dẫn? Không có kiểm tra về trường hợp đó và tôi tin rằng nó khá có khả năng - Windows sẽ sử dụng dấu gạch chéo ngược và args[0]có thể không thực sự là một đường dẫn.
Ben Hymers

Nếu '/' không tồn tại, rfind trả về -1, vì vậy "path" = aux.substr (0,0) và "name" = aux.substr (0): kết quả là đúng. Liên quan đến Windows, bạn nói đúng, '/' phải được đổi thành '\\', tôi cũng sẽ thay đổi để cho phép các cửa sổ. Tôi cũng đã thử nghiệm các tên tệp bằng '/', nhưng tên tệp cuối cùng này được mã hóa và không tạo ra sự cố.
Adrian Maire

1
Đó là phần về việc args[0]không nhất thiết phải là đường dẫn thực thi làm phiền tôi. Cảm ơn bạn đã sửa câu trả lời của bạn cho Windows :)
Bến Hymers

1
Nếu lệnh được chạy mà không đưa ra đường dẫn (tức là nó được tìm thấy bằng cách nằm trong thư mục được cho trong PATH env var), args [0] sẽ chỉ là tên của tệp thực thi, không có đường dẫn.
Kevin,

@Kevin: bạn (và những người khác) nói đúng, đây là một giải pháp đơn giản, dành cho các công cụ nhỏ, có hiệu quả ~ 95% trường hợp. Đối với phần mềm nghiêm túc, tệp cấu hình và / hoặc biến môi trường có lẽ tốt hơn. Ngoài ra, nhu cầu này thường ngụ ý một thiết kế không tốt (hoặc thậm chí sai).
Adrian Maire,


1

Những điều sau đây hoạt động như một giải pháp nhanh chóng và bẩn thỉu, nhưng lưu ý rằng nó còn lâu mới trở nên tuyệt vời:

#include <iostream>

using namespace std ;

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

17
Tôi đã thấy trên các câu hỏi SO khác rằng điều này không phải lúc nào cũng hoạt động và argv [0] có thể chứa đường dẫn tuyệt đối đến tệp thực thi, chỉ là tên tệp của tệp thực thi hoặc bất kỳ thứ gì khác.
Ben Hymers

7
Người ta không bao giờ nên tin tưởng argv [0] nếu họ đang cố gắng mở 'tệp hỗ trợ' hoặc tương tự. Argv có thể thay đổi và bất kỳ người gọi nào xấu xa đều có thể thay đổi giá trị của nó. Tránh trừ khi bạn đang sử dụng nó để ghi nhật ký, v.v., KHÔNG dùng để tạo đường dẫn được sử dụng để mở tệp.
Qix - MONICA ĐƯỢC HỖN HỢP 28/10/11

điều này không hoạt động trên Windows. argv [0] sẽ không có đường dẫn đầy đủ. Chỉ tệp .exe. Vui lòng không thử trong bash shell, hãy thử trong giao diện điều khiển tiêu chuẩn của ths và cout << argv [0] để tái tạo.
Freddy Martinez Garcia

@FreddyMartinezGarcia Vâng, tôi đã thử nghiệm nó trong Windows, vì vậy YMMV. Nó là bất cứ thứ gì được sử dụng để khởi chạy mã. Nếu bạn là tệp thực thi trong CWD chắc chắn bạn sẽ chỉ nhận được tên tệp.
Clifford

0

Trong trường hợp bạn cần xử lý đường dẫn unicode cho 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

Đối với Windows, bạn gặp vấn đề về cách tách tệp thực thi khỏi kết quả của GetModuleFileName(). Lệnh gọi API Windows PathRemoveFileSpec()mà Nate sử dụng cho mục đích đó trong câu trả lời của anh ấy đã thay đổi giữa Windows 8 và các phiên bản tiền nhiệm của nó. Vậy làm thế nào để vẫn tương thích với cả hai và an toàn? May mắn thay, có C ++ 17 (hoặc Boost, nếu bạn đang sử dụng trình biên dịch cũ hơn). Tôi làm việc này:

#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

Như những người khác đã đề cập, argv[0] là một giải pháp khá hay, miễn là nền tảng thực sự vượt qua đường dẫn thực thi, điều này chắc chắn có thể xảy ra không kém hệ điều hành là Windows (nơi WinAPI có thể giúp tìm đường dẫn thực thi). Nếu bạn muốn loại bỏ chuỗi để chỉ bao gồm đường dẫn đến thư mục chứa tệp thực thi, thì việc sử dụng đường dẫn đó để tìm các tệp ứng dụng khác (như nội dung trò chơi nếu chương trình của bạn là trò chơi) là hoàn toàn ổn, vì việc mở tệp liên quan đến thư mục làm việc hoặc thư mục gốc, nếu được cung cấp.


0

Đây là những gì tôi đã kết thúc với

Tệp tiêu đề trông giống như sau:

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

}

Thực hiện


#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

Thư viện SDL2 ( https://www.libsdl.org/ ) có hai chức năng được triển khai trên nhiều nền tảng:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Vì vậy, nếu bạn không muốn phát minh lại bánh xe ... thật đáng buồn, nó có nghĩa là bao gồm toàn bộ thư viện, mặc dù nó có giấy phép khá dễ dãi và người ta cũng có thể sao chép mã. Bên cạnh đó, nó cung cấp rất nhiều chức năng đa nền tảng khác.


0

Đây có lẽ là cách tự nhiên nhất để làm điều đó, đồng thời bao gồm hầu hết các nền tảng máy tính để bàn chính. Tôi không chắc chắn, nhưng tôi tin rằng điều này sẽ hoạt động với tất cả BSD, không chỉ FreeBSD, nếu bạn thay đổi kiểm tra macro nền tảng để bao gồm tất cả chúng. Nếu tôi đã từng cài đặt Solaris, tôi chắc chắn sẽ thêm nền tảng đó vào danh sách được hỗ trợ.

Có hỗ trợ UTF-8 đầy đủ trên Windows, điều mà không phải ai cũng quan tâm để tiến xa như vậy.

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

Điều này cho phép nhận được đường dẫn đầy đủ đến tệp thực thi của khá nhiều id quy trình, ngoại trừ trên Windows có một số quy trình có thuộc tính bảo mật sẽ không cho phép nó, vì vậy wysiwyg, giải pháp này không hoàn hảo.

Để giải quyết những gì câu hỏi đang hỏi một cách chính xác hơn, bạn có thể làm như sau:

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

Xây dựng cấu trúc tệp ở trên bằng lệnh này:

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

Để tải xuống bản sao của các tệp được liệt kê ở trên:

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

Để có thêm tính tốt liên quan đến quy trình đa nền tảng:

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

Xem readme để biết danh sách hầu hết các chức năng được bao gồm.


0

Nếu sử dụng C ++ 17, người ta có thể làm như sau để lấy đường dẫn đến tệp thực thi.

#include <filesystem>

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

Câu trả lời trên đã được thử nghiệm trên Debian 10 bằng G ++ 9.3.0


Lưu ý rằng điều này sẽ chỉ hoạt động nếu / proc / self / exe tồn tại và có thể truy cập được. Bạn có thể nên kiểm tra xem đây có phải là trường hợp không.
Zrin

-1

Kể từ C ++ 17:

Đảm bảo bạn bao gồm hệ thống tệp std.

#include <filesystem>

và bây giờ bạn có thể làm điều này.

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

hệ thống tập tin tăng cường trở thành một phần của lib tiêu chuẩn.

nếu bạn không thể tìm thấy nó, hãy thử xem bên dưới:

std::experimental::filesystem

10
Đây không phải là đường dẫn của tệp nhị phân, đó là thư mục làm việc hiện tại.
Zitrax

-2

Đây là giải pháp của tôi trong Windows. Nó được gọi như thế này:

std::wstring sResult = GetPathOfEXE(64);

Trong đó 64 là kích thước tối thiểu mà bạn nghĩ rằng đường dẫn sẽ có. GetPathOfEXE tự gọi đệ quy, tăng gấp đôi kích thước của bộ đệm mỗi lần cho đến khi nó nhận được bộ đệm đủ lớn để có được toàn bộ đường dẫn mà không bị cắt bớt.

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

Lý do sử dụng newvà (sai) là deletegì? Nếu bạn sử dụng a std::vector, mã của bạn sẽ không thể hiện hành vi không xác định.
IInspectable

Bên cạnh đó, GetModuleFileNameWkhông đặt mã lỗi cuối cùng trong trường hợp thành công. Mã đó bị phá vỡ theo nhiều cách. Đừng sử dụng nếu bạn tình cờ gặp phải điều này.
IInspectable

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

2
Đó là chỉ dành cho Windows và sử dụng MFC, rất xa so với nền tảng chéo, xin lỗi!
Ben Hymers

1
Đây thậm chí không phải là cách của Windows để làm điều đó. Hãy xem PathRemoveFileSpec()và các chức năng liên quan thay thế.
Remy Lebeau

-4

trong Unix (bao gồm cả Linux) hãy thử 'cái nào', trong Windows hãy thử 'ở đâu'.

#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

Phương pháp này hoạt động cho cả Windows và 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
Điều này trả về thư mục làm việc hiện tại, không phải là đường dẫn đến tệp thực thi có thể không giống nhau.
Dave Durbin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.