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

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


当前回答

在Linux上使用/proc/self/exe或argv[0]的另一种选择是使用ELF解释器传递的信息,由glibc提供:

#include <stdio.h>
#include <sys/auxv.h>

int main(int argc, char **argv)
{
    printf("%s\n", (char *)getauxval(AT_EXECFN));
    return(0);
}

请注意,getauxval是一个glibc扩展,为了健壮起见,您应该检查它是否返回NULL(表明ELF解释器没有提供AT_EXECFN参数),但我认为这在Linux上实际上从来都不是问题。

其他回答

要使其可靠地跨平台工作,需要使用#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();

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

根据QNX Neutrino版本的不同,有不同的方法来查找用于启动运行进程的可执行文件的完整路径和名称。我将进程标识符表示为<PID>。试试下面的方法:

If the file /proc/self/exefile exists, then its contents are the requested information. If the file /proc/<PID>/exefile exists, then its contents are the requested information. If the file /proc/self/as exists, then: open() the file. Allocate a buffer of, at least, sizeof(procfs_debuginfo) + _POSIX_PATH_MAX. Give that buffer as input to devctl(fd, DCMD_PROC_MAPDEBUG_BASE,.... Cast the buffer to a procfs_debuginfo*. The requested information is at the path field of the procfs_debuginfo structure. Warning: For some reason, sometimes, QNX omits the first slash / of the file path. Prepend that / when needed. Clean up (close the file, free the buffer, etc.). Try the procedure in 3. with the file /proc/<PID>/as. Try dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo) where dlinfo is a Dl_info structure whose dli_fname might contain the requested information.

我希望这能有所帮助。

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

但是我想知道是否有一种方便的方法来找到当前应用程序的目录在C/ c++与跨平台接口。

你不能这样做(至少在Linux上)

因为一个可执行文件可以,在运行它的进程执行期间,重命名(2)它的文件路径到不同的目录(同一文件系统)。请参见syscalls(2)和inode(7)。

在Linux上,一个可执行文件(原则上)甚至可以通过调用unlink(2)来删除(3)本身。Linux内核应该继续分配该文件,直到没有进程再引用它为止。使用proc(5),你可以做一些奇怪的事情(例如重命名(2)/proc/self/exe文件,等等…)

换句话说,在Linux上,“当前应用程序的目录”的概念没有任何意义。

请参阅高级Linux编程和操作系统:三个简单的部分。

还可以在OSDEV上查看一些开源操作系统(包括FreeBSD或GNU Hurd)。其中一些提供了接近POSIX的接口(API)。

考虑使用(在获得许可的情况下)跨平台c++框架,如Qt或POCO,也许可以通过将它们移植到您喜欢的操作系统来对它们做出贡献。