在我看来,Linux使用/proc/self/exe很容易但是我想知道是否有一种方便的方法来找到当前应用程序的目录在C/ c++与跨平台接口。我见过一些项目胡乱摆弄argv[0],但它似乎并不完全可靠。

如果你必须支持,比如说,Mac OS X,它没有/proc/,你会怎么做?使用#ifdefs来隔离平台特定的代码(例如NSBundle)?或者尝试从argv[0], $ path和诸如此类的东西中推断可执行文件的路径,冒着在边缘情况下发现错误的风险?


当前回答

您可以使用argv[0]并分析PATH环境变量。 看:一个可以找到自己的程序示例

其他回答

当然,这并不适用于所有项目。 尽管如此,QCoreApplication::applicationFilePath()在6年的c++ /Qt开发中从未让我失望过。

当然,在尝试使用它之前,应该彻底阅读文档:

警告:在Linux上,此函数将尝试从 /proc文件系统。如果失败,则假定argv[0]包含 可执行文件的绝对文件名。这个函数也假设 应用程序没有更改当前目录。

老实说,我认为#ifdef和其他类似的解决方案根本不应该在现代代码中使用。

我相信更小的跨平台库也存在。让他们把所有特定于平台的东西封装在里面。

Gregory Pakosz的whereami库使用mark4o文章中提到的api为各种平台实现了这一点。如果您“只是”需要一个适用于可移植项目的解决方案,而对各种平台的特性不感兴趣,那么这是最有趣的。

在撰写本文时,受支持的平台有:

窗户 Linux Mac iOS 安卓 QNX Neutrino FreeBSD NetBSD 蜻蜓BSD SunOS

该库由whereami.c和whereami.h组成,并在MIT和WTFPL2下获得许可。将文件放入您的项目中,包括头文件并使用它:

#include "whereami.h"

int main() {
  int length = wai_getExecutablePath(NULL, 0, NULL);
  char* path = (char*)malloc(length + 1);
  wai_getExecutablePath(path, length, &dirname_length);
  path[length] = '\0';

  printf("My path: %s", path);

  free(path);
  return 0;
}

要使其可靠地跨平台工作,需要使用#ifdef语句。

下面的代码可以在Windows、Linux、MacOS、Solaris或FreeBSD(尽管FreeBSD未经测试)中找到可执行文件的路径。它使用 Boost 1.55.0(或更高版本)来简化代码,但如果您愿意,可以很容易地删除它。只要根据操作系统和编译器的要求使用像_MSC_VER和__linux这样的定义即可。

#include <string>
#include <boost/predef/os.h>

#if (BOOST_OS_WINDOWS)
#  include <stdlib.h>
#elif (BOOST_OS_SOLARIS)
#  include <stdlib.h>
#  include <limits.h>
#elif (BOOST_OS_LINUX)
#  include <unistd.h>
#  include <limits.h>
#elif (BOOST_OS_MACOS)
#  include <mach-o/dyld.h>
#elif (BOOST_OS_BSD_FREE)
#  include <sys/types.h>
#  include <sys/sysctl.h>
#endif

/*
 * Returns the full path to the currently running executable,
 * or an empty string in case of failure.
 */
std::string getExecutablePath() {
    #if (BOOST_OS_WINDOWS)
        char *exePath;
        if (_get_pgmptr(&exePath) != 0)
            exePath = "";
    #elif (BOOST_OS_SOLARIS)
        char exePath[PATH_MAX];
        if (realpath(getexecname(), exePath) == NULL)
            exePath[0] = '\0';
    #elif (BOOST_OS_LINUX)
        char exePath[PATH_MAX];
        ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath));
        if (len == -1 || len == sizeof(exePath))
            len = 0;
        exePath[len] = '\0';
    #elif (BOOST_OS_MACOS)
        char exePath[PATH_MAX];
        uint32_t len = sizeof(exePath);
        if (_NSGetExecutablePath(exePath, &len) != 0) {
            exePath[0] = '\0'; // buffer too small (!)
        } else {
            // resolve symlinks, ., .. if possible
            char *canonicalPath = realpath(exePath, NULL);
            if (canonicalPath != NULL) {
                strncpy(exePath,canonicalPath,len);
                free(canonicalPath);
            }
        }
    #elif (BOOST_OS_BSD_FREE)
        char exePath[2048];
        int mib[4];  mib[0] = CTL_KERN;  mib[1] = KERN_PROC;  mib[2] = KERN_PROC_PATHNAME;  mib[3] = -1;
        size_t len = sizeof(exePath);
        if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0)
            exePath[0] = '\0';
    #endif
        return std::string(exePath);
}

上面的版本返回包括可执行文件名称在内的完整路径。如果相反,你想要没有可执行名称的路径,#include boost/ filessystem .hpp>,并将返回语句更改为:

return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();

依我看,没有这种办法。还有一个不明确的地方:如果同一个可执行文件有多个硬链接指向它,您希望得到什么作为答案?(硬链接实际上并不“指向”,它们是同一个文件,只是位于文件系统层次结构中的另一个位置。)

一旦execve()成功执行了一个新的二进制文件,所有关于原始程序参数的信息都将丢失。

如果您正在编写GPLed代码并使用GNU autotools,那么在许多操作系统(包括Windows和macOS)上处理细节的可移植方法是gnulib的reloctable -prog模块。