提问人:Ashwin Nanjappa 提问时间:9/27/2008 最后编辑:Adrian McCarthyAshwin Nanjappa 更新时间:10/28/2023 访问量:567655
如何获取运行程序的目录?
How do I get the directory that a program is running from?
问:
是否有与平台无关和文件系统无关的方法来获取使用 C/C++ 运行程序的目录的完整路径?不要与当前工作目录混淆。(请不要推荐库,除非它们是标准的库,如 clib 或 STL。
(如果没有与平台/文件系统无关的方法,也欢迎在 Windows 和 Linux 中针对特定文件系统提供建议。
答:
对于 Win32,GetCurrentDirectory 应该可以解决问题。
评论
不,没有标准方法。我相信 C/C++ 标准甚至没有考虑目录(或其他文件系统组织)的存在。
在 Windows 上,当 hModule 参数设置为 NULL 时,GetModuleFileName() 将返回当前进程的可执行文件的完整路径。我对 Linux 无能为力。
此外,您应该明确您是想要当前目录还是程序映像/可执行文件所在的目录。就目前而言,你的问题在这一点上有点模棱两可。
如果你想要一个没有库的标准方式:不可以。目录的整个概念不包括在标准中。
如果您同意对接近标准的库的某些(可移植)依赖是可以的:使用 Boost 的文件系统库并询问 initial_path()。
恕我直言,这是尽可能接近的,具有良好的业力(Boost 是一套完善的高质量库)
评论
在 POSIX 平台上,你可以使用 getcwd()。
在 Windows 上,您可以使用 _getcwd(),因为 getcwd() 的使用已被弃用。
对于标准库,如果 Boost 对你来说足够标准,我会建议 Boost::filesystem,但他们似乎已经从提案中删除了路径规范化。您可能需要等到 TR2 变得容易获得完全标准的解决方案。
评论
也许将当前工作目录与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
评论
您不能为此目的使用 argv[0],通常它确实包含可执行文件的完整路径,但不能 - 可以在字段中使用任意值创建进程。
还要注意,当前目录和带有可执行文件的目录是两个不同的东西,所以 getcwd() 也不会帮助你。
在 Windows 上使用 GetModuleFileName(),在 Linux 上读取 /dev/proc/procID/.。文件。
Boost 文件系统的行为与 POSIX 的行为类似,并且您自己也不需要,但附加到它们中的任何一个都应该这样做。initial_path()
getcwd()
argv[0]
你可能会注意到结果并不总是很漂亮 - 你可能会得到类似 or 的东西,但我相信它总是会产生一个有效的路径来命名可执行文件(请注意,路径中的连续斜杠折叠为 1)。/foo/bar/../../baz/a.out
/foo/bar//baz/a.out
我之前使用(第三个论点在 Linux 上有效,但在 Windows 上似乎不可行,所以我基本上推荐与其他人以前做过的解决方案相同,但额外解释了为什么它实际上是正确的,即使结果不漂亮。envp
main()
如果在程序首次启动时获取当前目录,则实际上拥有启动程序的目录。将值存储在变量中,稍后在程序中引用它。这与保存当前可执行程序文件的目录不同。它不一定是同一个目录;如果有人从命令提示符运行该程序,则该程序将从命令提示符的当前工作目录运行,即使该程序文件位于其他位置。
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);
评论
下面是获取执行应用的完整路径的代码:Here's code to get the full path to the executing app:
变量声明:
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;
评论
/proc
/proc
char pBuf[256]; size_t len = sizeof(pBuf);
只是为了姗姗来迟地堆积在这里,...
没有标准的解决方案,因为这些语言与底层文件系统无关,所以正如其他人所说,基于目录的文件系统的概念超出了 C/C++ 语言的范围。
最重要的是,你想要的不是当前的工作目录,而是运行程序的目录,这必须考虑到程序是如何到达它的位置的——即它是否通过分支作为新进程生成,等等。正如解决方案所演示的那样,要获取运行程序的目录,需要您从相关操作系统的进程控制结构中获取该信息,这是这个问题的唯一权威。因此,根据定义,它是一个特定于操作系统的解决方案。
正如Minok所提到的,C标准或C++标准中没有指定这样的功能。这被认为是纯粹的特定于操作系统的功能,例如,它在 POSIX 标准中指定。
Thorsten79 给出了很好的建议,它是 Boost.Filesystem 库。但是,如果您不想为程序提供任何二进制形式的链接时间依赖项,这可能会带来不便。
我推荐的一个不错的选择是收集 100% 仅标头的 STLSoft C++ 库 Matthew Wilson(关于 C++ 的必读书籍的作者)。有可移植的外观 PlatformSTL 允许访问特定于系统的 API:WinSTL for Windows 和 UnixSTL on Unix,因此它是可移植的解决方案。所有特定于系统的元素都是使用特征和策略指定的,因此它是可扩展的框架。当然,还提供了文件系统库。
在 Windows 上,最简单的方法是使用函数 in 获取指向字符串的指针,该字符串表示可执行文件的绝对路径,包括可执行文件名称。_get_pgmptr
stdlib.h
char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe
评论
对于 Windows 系统,您可以在控制台使用 system() 命令。控制台为您提供有关目录等的信息。有关该命令的信息,请参见。但是对于类 Unix 系统,我不知道......如果运行此命令,请读取 bash 命令。 不显示目录...dir
dir
cmd
ls
例:
int main()
{
system("dir");
system("pause"); //this wait for Enter-key-press;
return 0;
}
这是来自cplusplus论坛
在 Windows 上:
#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 );
}
评论
error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'
#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);
}
评论
对于相对路径,这是我所做的。我知道这个问题的年龄,我只想贡献一个更简单的答案,在大多数情况下都有效:
假设你有一条这样的路径:
"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 中)
我知道现在给出答案已经很晚了,但我发现没有一个答案像我自己的解决方案那样有用。获取从 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("\\"));
我认为这应该有效但尚未测试,因此如果它有效或修复,将不胜感激。
评论
argv[0]
这是一个非常好的主意,但不幸的是,我在 Linux 上得到的是“./my_executable_name”或“./make/my_executable_name”。基本上,我得到什么完全取决于我如何启动它
文件系统 TS 现在是一个标准(并受 gcc 5.3+ 和 clang 3.9+ 支持),因此您可以使用其中的 current_path()
函数:
std::string path = std::experimental::filesystem::current_path();
在 gcc (5.3+) 中,要包含文件系统,您需要使用:
#include <experimental/filesystem>
并将你的代码与标志链接。-lstdc++fs
如果要将文件系统与 Microsoft Visual Studio 一起使用,请阅读此内容。
评论
1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs.
linux bash 命令,progname 将报告程序的路径。
即使可以从程序中发出 which 命令并将输出定向到 tmp 文件和程序 随后读取该TMP文件,它不会告诉您该程序是否是正在执行的程序。它只告诉您具有该名称的程序所在的位置。
需要的是获取进程 ID 号,并解析出名称的路径
在我的程序中,我想知道该程序是否是 从用户的 bin 目录或路径中的其他目录执行 或从 /usr/bin。/usr/bin 将包含支持的版本。 我的感觉是,在 Linux 中,有一种可移植的解决方案。
一个库解决方案(尽管我知道这不是要求的)。
如果您碰巧使用Qt:QCoreApplication::applicationDirPath()
从C++11开始,使用实验文件系统,C++14-C++17以及使用官方文件系统。
application.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
}
评论
std
是未定义的行为。为了避免这种情况,您可以同时添加命名空间和您选择的第三个命名空间,或者如果您不介意将声明添加到全局命名空间,则直接使用 。std::filesystem
std::experimental::filesystem
using std::filesystem::path
path
只是我的两分钱,但以下代码在 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已经存在很多年了。
评论
当前 .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;
评论
GetModuleFileName
GetModuleFileNameA
GetModuleFileNameW
以下内容在 macOS 10.15.7 上对我来说效果很好
brew install boost
主 .cpp
#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
评论
argv[0]
#include <windows.h>
char*
_pgmptr
_pgmptr
_pgmptr
_wpgmptr
_get_pgmptr(char**)
_get_wpgmptr(wchar_t**)