是否有一种与平台无关和与文件系统无关的方法来获取使用 C/C++ 运行程序的目录的完整路径?不要与当前工作目录混淆。(请不要推荐库,除非它们是标准库,如 clib 或 STL。)
(如果没有与平台/文件系统无关的方法,也欢迎在 Windows 和 Linux 中针对特定文件系统工作的建议。)
是否有一种与平台无关和与文件系统无关的方法来获取使用 C/C++ 运行程序的目录的完整路径?不要与当前工作目录混淆。(请不要推荐库,除非它们是标准库,如 clib 或 STL。)
(如果没有与平台/文件系统无关的方法,也欢迎在 Windows 和 Linux 中针对特定文件系统工作的建议。)
这是获取执行应用程序的完整路径的代码:
视窗:
char pBuf[256];
size_t len = sizeof(pBuf);
int bytes = GetModuleFileName(NULL, pBuf, len);
return bytes ? bytes : -1;
Linux:
int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
if(bytes >= 0)
pBuf[bytes] = '\0';
return bytes;
如果您在程序第一次启动时获取当前目录,那么您实际上拥有了启动程序的目录。将值存储在变量中,稍后在程序中引用它。这与保存当前可执行程序文件的目录不同。它不一定是同一个目录;如果有人从命令提示符运行程序,那么即使程序文件位于其他位置,程序也会从命令提示符的当前工作目录运行。
getcwd 是一个 POSIX 函数,所有兼容 POSIX 的平台都支持开箱即用。您不必做任何特别的事情(除了在 Unix 上包含正确的头文件 unistd.h 和在 Windows 上包含 direct.h )。
由于您正在创建一个 C 程序,它将与默认的 c 运行时库链接,该库由系统中的所有进程链接(避免特制的异常),并且默认情况下将包含此函数。CRT 从未被视为外部库,因为它为操作系统提供了基本的标准兼容接口。
在 Windows 上,getcwd 函数已被弃用,取而代之的是 _getcwd。我认为您可以以这种方式使用它。
#include <stdio.h> /* defines FILENAME_MAX */
#ifdef WINDOWS
#include <direct.h>
#define GetCurrentDir _getcwd
#else
#include <unistd.h>
#define GetCurrentDir getcwd
#endif
char cCurrentPath[FILENAME_MAX];
if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
{
return errno;
}
cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */
printf ("The current working directory is %s", cCurrentPath);
这是来自cplusplus 论坛
在窗户上:
#include <string>
#include <windows.h>
std::string getexepath()
{
char result[ MAX_PATH ];
return std::string( result, GetModuleFileName( NULL, result, MAX_PATH ) );
}
在 Linux 上:
#include <string>
#include <limits.h>
#include <unistd.h>
std::string getexepath()
{
char result[ PATH_MAX ];
ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
return std::string( result, (count > 0) ? count : 0 );
}
在 HP-UX 上:
#include <string>
#include <limits.h>
#define _PSTAT64
#include <sys/pstat.h>
#include <sys/types.h>
#include <unistd.h>
std::string getexepath()
{
char result[ PATH_MAX ];
struct pst_status ps;
if (pstat_getproc( &ps, sizeof( ps ), 0, getpid() ) < 0)
return std::string();
if (pstat_getpathname( result, PATH_MAX, &ps.pst_fid_text ) < 0)
return std::string();
return std::string( result );
}
如果您想要一种没有库的标准方式:不。目录的整个概念不包含在标准中。
如果您同意对接近标准库的某些(可移植)依赖是可以的:使用Boost 的文件系统库并请求initial_path()。
恕我直言,你可以得到尽可能接近,具有良好的业力(Boost 是一套完善的高质量库)
我知道在这一天给出答案已经很晚了,但我发现没有一个答案对我来说像我自己的解决方案那样有用。获取从 CWD 到 bin 文件夹的路径的一种非常简单的方法是:
int main(int argc, char* argv[])
{
std::string argv_str(argv[0]);
std::string base = argv_str.substr(0, argv_str.find_last_of("/"));
}
您现在可以将其用作相对路径的基础。所以例如我有这个目录结构:
main
----> test
----> src
----> bin
我想将我的源代码编译到 bin 并写一个日志来测试我可以将这一行添加到我的代码中。
std::string pathToWrite = base + "/../test/test.log";
我已经使用完整路径、别名等在 Linux 上尝试了这种方法,并且效果很好。
笔记:
如果您在 Windows 上,则应使用“\”作为文件分隔符,而不是“/”。你也必须逃避这个,例如:
std::string base = argv[0].substr(0, argv[0].find_last_of("\\"));
我认为这应该可行,但尚未测试,因此如果可行,请发表评论,如果不可行,请进行修复。
文件系统 TS现在是一个标准(gcc 5.3+ 和 clang 3.9+ 支持),所以你可以使用current_path()
它的函数:
std::string path = std::experimental::filesystem::current_path();
在 gcc (5.3+) 中包含 Filesystem 您需要使用:
#include <experimental/filesystem>
并将您的代码与-lstdc++fs
标志链接。
如果您想将 Filesystem 与 Microsoft Visual Studio 一起使用,请阅读此.
不,没有标准的方法。我相信 C/C++ 标准甚至不考虑目录(或其他文件系统组织)的存在。
在 Windows 上,当hModule参数设置为NULL时, GetModuleFileName()将返回当前进程的可执行文件的完整路径。我对 Linux 无能为力。
此外,您应该澄清您想要当前目录还是程序图像/可执行文件所在的目录。就目前而言,您的问题在这一点上有点模棱两可。
在 Windows 上,最简单的方法是使用_get_pgmptr
函数 instdlib.h
获取指向字符串的指针,该字符串表示可执行文件的绝对路径,包括可执行文件的名称。
char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe
也许将当前工作目录与 argv[0] 连接起来?我不确定这是否适用于 Windows,但它适用于 linux。
例如:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv) {
char the_path[256];
getcwd(the_path, 255);
strcat(the_path, "/");
strcat(the_path, argv[0]);
printf("%s\n", the_path);
return 0;
}
运行时,它输出:
jeremy@jeremy-desktop:~/Desktop$ ./test
/home/jeremy/Desktop/./test
对于 Win32 GetCurrentDirectory应该可以解决问题。
您不能为此目的使用 argv[0],通常它确实包含可执行文件的完整路径,但不是必须的 - 可以在字段中使用任意值创建进程。
另外请注意,当前目录和包含可执行文件的目录是两个不同的东西,因此 getcwd() 也无济于事。
在 Windows 上使用 GetModuleFileName(),在 Linux 上读取 /dev/proc/ procID /.. 文件。
只是我的两分钱,但以下代码不能在 C++17 中可移植地工作吗?
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main(int argc, char* argv[])
{
std::cout << "Path is " << fs::path(argv[0]).parent_path() << '\n';
}
似乎至少在 Linux 上对我有用。
基于之前的想法,我现在有:
std::filesystem::path prepend_exe_path(const std::string& filename, const std::string& exe_path = "");
通过实施:
fs::path prepend_exe_path(const std::string& filename, const std::string& exe_path)
{
static auto exe_parent_path = fs::path(exe_path).parent_path();
return exe_parent_path / filename;
}
和初始化技巧main()
:
(void) prepend_exe_path("", argv[0]);
感谢@Sam Redway 的 argv[0] 想法。当然,我知道当 OP 提出这个问题时,C++17 已经存在很多年了。
只是为了迟来的堆积在这里,...
没有标准的解决方案,因为这些语言与底层文件系统无关,所以正如其他人所说,基于目录的文件系统的概念超出了 c / c++ 语言的范围。
最重要的是,您想要的不是当前工作目录,而是程序正在运行的目录,这必须考虑程序如何到达它的位置 - 即它是否通过 fork 等作为新进程产生。如解决方案所示,要获取程序运行所在的目录,需要您从相关操作系统的进程控制结构中获取该信息,这是该问题的唯一权威。因此,根据定义,它是特定于操作系统的解决方案。
对于控制台上的 Windows 系统,您可以使用 system( dir
) 命令。控制台为您提供有关目录等的信息。dir
在cmd
. 但是对于类 Unix 系统,我不知道……如果运行此命令,请阅读 bash 命令。ls
不显示目录...
例子:
int main()
{
system("dir");
system("pause"); //this wait for Enter-key-press;
return 0;
}
#include <windows.h>
using namespace std;
// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
const unsigned long maxDir = 260;
char currentDir[maxDir];
GetCurrentDirectory(maxDir, currentDir);
return string(currentDir);
}
从 C++11 开始,使用实验文件系统,C++14-C++17 以及使用官方文件系统。
应用程序.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();
应用程序.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
}
对于相对路径,这就是我所做的。我知道这个问题的年龄,我只是想提供一个在大多数情况下都有效的更简单的答案:
假设您有这样的路径:
"path/to/file/folder"
出于某种原因,在 Eclipse 中制作的 Linux 构建的可执行文件可以正常工作。但是,如果给定这样的路径,Windows 会变得非常困惑!
如上所述,有几种方法可以获取可执行文件的当前路径,但我发现在大多数情况下最简单的方法是将其附加到路径的前面:
"./path/to/file/folder"
只需添加“./”即可让您排序!:) 然后你可以从你想要的任何目录开始加载,只要它与可执行文件本身一起。
编辑:如果您尝试从 code::blocks 启动可执行文件,如果那是正在使用的开发环境,这将不起作用,因为某种原因,code::blocks 不能正确加载东西......:D
EDIT2:我发现的一些新东西是,如果您在代码中指定像这样的静态路径(假设 Example.data 是您需要加载的东西):
"resources/Example.data"
如果您然后从实际目录启动您的应用程序(或在 Windows 中,您创建一个快捷方式,并将工作目录设置为您的应用程序目录),那么它将像这样工作。在调试与缺少资源/文件路径相关的问题时请记住这一点。(特别是在从 IDE 启动构建 exe 时设置错误工作目录的 IDE 中)
图书馆解决方案(虽然我知道这不是要求)。如果你碰巧使用 Qt:
QCoreApplication::applicationDirPath()
当前 .exe 的路径
#include <Windows.h>
std::wstring getexepathW()
{
wchar_t result[MAX_PATH];
return std::wstring(result, GetModuleFileNameW(NULL, result, MAX_PATH));
}
std::wcout << getexepathW() << std::endl;
// -------- OR --------
std::string getexepathA()
{
char result[MAX_PATH];
return std::string(result, GetModuleFileNameA(NULL, result, MAX_PATH));
}
std::cout << getexepathA() << std::endl;
Boost Filesystem's 的initial_path()
行为类似于 POSIX's getcwd()
,并且您自己也不需要,但附加argv[0]
到它们中的任何一个都应该这样做。
您可能会注意到结果并不总是很漂亮——您可能会得到类似/foo/bar/../../baz/a.out
or的东西/foo/bar//baz/a.out
,但我相信它总是会产生一个有效的路径来命名可执行文件(请注意,路径中的连续斜杠折叠为一个)。
envp
我之前使用(第三个参数main()
在 Linux 上工作但在 Windows 上似乎不工作) 编写了一个解决方案,所以我基本上推荐与其他人之前所做的相同的解决方案,但额外解释了为什么它实际上是正确的即使结果并不漂亮。
正如Minok 所提到的,没有指定 ini C 标准或 C++ 标准的此类功能。这被认为是纯粹的操作系统特定功能,例如在 POSIX 标准中指定。
Thorsten79给出了很好的建议,它是 Boost.Filesystem 库。但是,如果您不想为您的程序提供任何二进制形式的链接时依赖项,这可能会带来不便。
我推荐的一个不错的选择是收集 100% 仅标头的STLSoft C++ 库 Matthew Wilson(有关 C++ 的必读书籍的作者)。有可移植的门面 PlatformSTL 可以访问系统特定的 API:Windows 的 WinSTL 和 Unix 的 UnixSTL,因此它是可移植的解决方案。所有特定于系统的元素都使用特征和策略指定,因此它是可扩展的框架。当然,提供了文件系统库。
progname的 linux bash 命令 将报告程序的路径。
即使可以从您的程序中发出 which 命令并将输出定向到 tmp 文件,并且程序随后读取该 tmp 文件,它也不会告诉您该程序是否正在执行。它只告诉您具有该名称的程序所在的位置。
需要的是获取您的进程 ID 号,并解析出名称的路径
在我的程序中,我想知道程序是从用户的 bin 目录还是从路径中的另一个目录或 /usr/bin 执行的。/usr/bin 将包含支持的版本。我的感觉是,在 Linux 中有一种可移植的解决方案。
realpath()
像这样使用stdlib.h
:
char *working_dir_path = realpath(".", NULL);
以下对我来说在 macOS 10.15.7 上运行良好
brew install boost
主文件
#include <iostream>
#include <boost/filesystem.hpp>
int main(int argc, char* argv[]){
boost::filesystem::path p{argv[0]};
p = absolute(p).parent_path();
std::cout << p << std::endl;
return 0;
}
编译
g++ -Wall -std=c++11 -l boost_filesystem main.cpp