我认为Linux的/proc/self/exe很容易使用。但我想知道是否有跨平台接口的方便方法来查找当前应用程序的目录。我见过一些项目在argv[0]上操作,但它似乎不是完全可靠的。
如果您曾经需要支持例如Mac OS X这样没有/proc/的系统,您会怎么做?使用#ifdef来隔离特定于平台的代码(例如NSBundle)吗?还是尝试从argv [0],$PATH等中推断出可执行文件的路径,冒着在边缘情况下发现错误的风险?
我认为Linux的/proc/self/exe很容易使用。但我想知道是否有跨平台接口的方便方法来查找当前应用程序的目录。我见过一些项目在argv[0]上操作,但它似乎不是完全可靠的。
如果您曾经需要支持例如Mac OS X这样没有/proc/的系统,您会怎么做?使用#ifdef来隔离特定于平台的代码(例如NSBundle)吗?还是尝试从argv [0],$PATH等中推断出可执行文件的路径,冒着在边缘情况下发现错误的风险?
QCoreApplication::applicationFilePath()
从未让我失望。#ifdef
和其他解决方案在现代代码中根本不应该使用。你可以使用 argv[0] 并分析 PATH 环境变量。 请参考:一个可以找到自身的程序示例
execv
等函数会将可执行文件路径与 argv
分开处理。 - dmckee --- ex-moderator kitten由于可执行文件可以在运行它的进程执行期间将其文件路径重命名为同一文件系统中的不同目录。另请参见syscalls(2)和inode(7)。
但我想知道是否有一种方便的方法来使用跨平台接口在C/C++中查找当前应用程序的目录。
在Linux上,一个可执行文件甚至可以(原则上)通过调用unlink(2)来 remove(3) 自己。然后Linux内核应该保留文件分配,直到没有进程再引用它为止。使用proc(5)你可以做很奇怪的事情(例如rename(2)那个 /proc/self/exe
文件等...)
换句话说,在Linux上,“当前应用程序目录”的概念没有任何意义。
还可以阅读Advanced Linux Programming和Operating Systems: Three Easy Pieces获取更多信息。
还可以在OSDEV上查看多个开源操作系统(包括FreeBSD或GNU Hurd)。其中一些提供接口(API)与 POSIX 接口相近。
仅供参考。您可以使用此代码在C/C++中通过跨平台接口找到当前应用程序的目录。
void getExecutablePath(char ** path, unsigned int * pathLength)
{
// Early exit when invalid out-parameters are passed
if (!checkStringOutParameter(path, pathLength))
{
return;
}
#if defined SYSTEM_LINUX
// Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
char exePath[PATH_MAX];
// Return written bytes, indicating if memory was sufficient
int len = readlink("/proc/self/exe", exePath, PATH_MAX);
if (len <= 0 || len == PATH_MAX) // memory not sufficient or general error occured
{
invalidateStringOutParameter(path, pathLength);
return;
}
// Copy contents to caller, create caller ownership
copyToStringOutParameter(exePath, len, path, pathLength);
#elif defined SYSTEM_WINDOWS
// Preallocate MAX_PATH (e.g., 4095) characters and hope the executable path isn't longer (including null byte)
char exePath[MAX_PATH];
// Return written bytes, indicating if memory was sufficient
unsigned int len = GetModuleFileNameA(GetModuleHandleA(0x0), exePath, MAX_PATH);
if (len == 0) // memory not sufficient or general error occured
{
invalidateStringOutParameter(path, pathLength);
return;
}
// Copy contents to caller, create caller ownership
copyToStringOutParameter(exePath, len, path, pathLength);
#elif defined SYSTEM_SOLARIS
// Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
char exePath[PATH_MAX];
// Convert executable path to canonical path, return null pointer on error
if (realpath(getexecname(), exePath) == 0x0)
{
invalidateStringOutParameter(path, pathLength);
return;
}
// Copy contents to caller, create caller ownership
unsigned int len = strlen(exePath);
copyToStringOutParameter(exePath, len, path, pathLength);
#elif defined SYSTEM_DARWIN
// Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
char exePath[PATH_MAX];
unsigned int len = (unsigned int)PATH_MAX;
// Obtain executable path to canonical path, return zero on success
if (_NSGetExecutablePath(exePath, &len) == 0)
{
// Convert executable path to canonical path, return null pointer on error
char * realPath = realpath(exePath, 0x0);
if (realPath == 0x0)
{
invalidateStringOutParameter(path, pathLength);
return;
}
// Copy contents to caller, create caller ownership
unsigned int len = strlen(realPath);
copyToStringOutParameter(realPath, len, path, pathLength);
free(realPath);
}
else // len is initialized with the required number of bytes (including zero byte)
{
char * intermediatePath = (char *)malloc(sizeof(char) * len);
// Convert executable path to canonical path, return null pointer on error
if (_NSGetExecutablePath(intermediatePath, &len) != 0)
{
free(intermediatePath);
invalidateStringOutParameter(path, pathLength);
return;
}
char * realPath = realpath(intermediatePath, 0x0);
free(intermediatePath);
// Check if conversion to canonical path succeeded
if (realPath == 0x0)
{
invalidateStringOutParameter(path, pathLength);
return;
}
// Copy contents to caller, create caller ownership
unsigned int len = strlen(realPath);
copyToStringOutParameter(realPath, len, path, pathLength);
free(realPath);
}
#elif defined SYSTEM_FREEBSD
// Preallocate characters and hope the executable path isn't longer (including null byte)
char exePath[2048];
unsigned int len = 2048;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
// Obtain executable path by syscall
if (sysctl(mib, 4, exePath, &len, 0x0, 0) != 0)
{
invalidateStringOutParameter(path, pathLength);
return;
}
// Copy contents to caller, create caller ownership
copyToStringOutParameter(exePath, len, path, pathLength);
#else
// If no OS could be detected ... degrade gracefully
invalidateStringOutParameter(path, pathLength);
#endif
}
您可以在此处详细查看。
getprogname()
是更符合惯用方式的方法(需要包含<stdlib.h>
,因为它是libc的一部分)。 - BitTickler我在标准中没有找到它的工作方式,但在C++17及以后的版本中,我测试过的所有平台上都可以使用:
std::filesystem::canonical(argv[0])
尽管argv [0]
根据平台包含不同的内容,但在规范化后,它仍然有效。它提供了可执行文件的绝对路径,无论当前工作目录如何。
/usr/local/bin
中的二进制文件)时才会执行。 - undefined
ps -o comm
。 让我来到这里的是:"/proc/pid/path/a.out"。 - basinstd::filesystem::current_path()
不会返回当前可执行文件的路径,而是返回当前工作目录的路径。 - Ted Lyngmo