如何获取运行程序的目录?

How do I get the directory that a program is running from?

提问人:Ashwin Nanjappa 提问时间:9/27/2008 最后编辑:Adrian McCarthyAshwin Nanjappa 更新时间:10/28/2023 访问量:567655

问:

是否有与平台无关和文件系统无关的方法来获取使用 C/C++ 运行程序的目录的完整路径?不要与当前工作目录混淆。(请不要推荐库,除非它们是标准的库,如 clib 或 STL。

(如果没有与平台/文件系统无关的方法,也欢迎在 Windows 和 Linux 中针对特定文件系统提供建议。

C++ C 可执行文件路径

评论

4赞 David R Tribble 1/28/2010
除非你能可靠地从中提取路径,否则该技术将非常依赖于操作系统。argv[0]
2赞 colemik 4/24/2013
澄清一下:“当前目录”,或者“运行程序的目录”(在问题的术语中)是程序的图像文件(~.exe文件)所在的目录,而“当前工作目录”是目录,如果程序使用相对路径,则会自动完成?
4赞 rsethc 5/1/2013
当您 时,Windows 会自动将 的可执行路径放入 中。如果您仅在 Windows 上工作,则无需调用额外的函数或假设垃圾。#include <windows.h>char*_pgmptr
0赞 Gregory Pakosz 6/5/2015
这回答了你最初的问题吗?github.com/gpakosz/whereami
3赞 Hydranix 3/6/2016
虽然评论是三年前的,但我想扩展一下 rsethc 关于 .MSDN 文档指出,不推荐使用 and 变量,应改用该函数 或。MSDN(默沙纳)_pgmptr_pgmptr_wpgmptr_get_pgmptr(char**)_get_wpgmptr(wchar_t**)

答:

7赞 Torbjörn Gyllebring 9/27/2008 #1

对于 Win32,GetCurrentDirectory 应该可以解决问题。

评论

0赞 McLeary 4/16/2020
此函数有一个很大的问题多线程应用程序和共享库代码不应使用 GetCurrentDirectory 函数,并且应避免使用相对路径名。如果你能接受这个假设,那么它是最好的解决方案。
10赞 Michael Burr 9/27/2008 #2

不,没有标准方法。我相信 C/C++ 标准甚至没有考虑目录(或其他文件系统组织)的存在。

在 Windows 上,当 hModule 参数设置为 NULL 时,GetModuleFileName() 将返回当前进程的可执行文件的完整路径。我对 Linux 无能为力。

此外,您应该明确您是想要当前目录还是程序映像/可执行文件所在的目录。就目前而言,你的问题在这一点上有点模棱两可。

30赞 Thorsten79 9/27/2008 #3

如果你想要一个没有库的标准方式:不可以。目录的整个概念不包括在标准中。

如果您同意对接近标准的库的某些(可移植)依赖是可以的:使用 Boost 的文件系统库并询问 initial_path()。

恕我直言,这是尽可能接近的,具有良好的业力(Boost 是一套完善的高质量库)

评论

8赞 Jonathan Leffler 9/28/2008
来自 Boost 文档:template <class Path> const Path& initial_path();返回:current_path() 在输入 main() 时。而 current_path() 是 '好像通过 POSIX getcwd()'。这不是提问者所要求的。
0赞 moala 4/22/2011
请参阅 boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/... 了解 Boost 1.46.1
1赞 jpo38 6/22/2016
如前所述,这给出了调用二进制文件的路径,而不是二进制文件的路径......因为它可以从不同的文件夹启动。
0赞 Fruny 9/27/2008 #4

在 POSIX 平台上,你可以使用 getcwd()。

在 Windows 上,您可以使用 _getcwd(),因为 getcwd() 的使用已被弃用。

对于标准库,如果 Boost 对你来说足够标准,我会建议 Boost::filesystem,但他们似乎已经从提案中删除了路径规范化。您可能需要等到 TR2 变得容易获得完全标准的解决方案。

评论

11赞 Jonathan Leffler 9/28/2008
getcwd() 没有按照提问者的要求去做。
0赞 Sнаđошƒаӽ 4/20/2015
是不是被接受的答案使用了 getcwd(),还是我不只是理解?
0赞 Arnaud 6/26/2015
我投了赞成票,因为你是第一个提出被认为是正确答案的人。
0赞 SmallChess 3/22/2018
这个答案甚至没有试图解决这个问题。写它很丢人。
8赞 Paige Ruten 9/27/2008 #5

也许将当前工作目录与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

评论

0赞 Michael Burr 9/27/2008
您需要检查 argv[0] 中是否给出了绝对路径。但更重要的是,如果图像是通过 PATH 定位的呢?linux 是填写完整路径还是只填写命令行上的内容?
0赞 Jonathan Leffler 9/28/2008
正如Mike B所指出的,这是一个非普遍的解决方案;它仅在一些非常有限的情况下有效。基本上,只有当你通过相对路径名运行命令时 - 当你运行 ../../../bin/progname 而不是 ./test
0赞 jpo38 6/22/2016
如果解析 argv[0] 与当前目录相比的可能相对路径(因为 argv[0] 可能是“../../myprogram.exe“),这可能是回答问题的最安全方法。它将始终工作并且是便携式的(它甚至可以在 Android 上运行!
6赞 eugensk 9/27/2008 #6

您不能为此目的使用 argv[0],通常它确实包含可执行文件的完整路径,但不能 - 可以在字段中使用任意值创建进程。

还要注意,当前目录和带有可执行文件的目录是两个不同的东西,所以 getcwd() 也不会帮助你。

在 Windows 上使用 GetModuleFileName(),在 Linux 上读取 /dev/proc/procID/.。文件。

0赞 John Zwinck 9/28/2008 #7

Boost 文件系统的行为与 POSIX 的行为类似,并且您自己也不需要,但附加到它们中的任何一个都应该这样做。initial_path()getcwd()argv[0]

你可能会注意到结果并不总是很漂亮 - 你可能会得到类似 or 的东西,但我相信它总是会产生一个有效的路径来命名可执行文件(请注意,路径中的连续斜杠折叠为 1)。/foo/bar/../../baz/a.out/foo/bar//baz/a.out

我之前使用(第三个论点在 Linux 上有效,但在 Windows 上似乎不可行,所以我基本上推荐与其他人以前做过的解决方案相同,但额外解释了为什么它实际上是正确的,即使结果不漂亮。envpmain()

172赞 computinglife 9/28/2008 #8

如果在程序首次启动时获取当前目录,则实际上拥有启动程序的目录。将值存储在变量中,稍后在程序中引用它。这与保存当前可执行程序文件的目录不同。它不一定是同一个目录;如果有人从命令提示符运行该程序,则该程序将从命令提示符的当前工作目录运行,即使程序文件位于其他位置。

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);

评论

49赞 Michael Burr 10/1/2008
很好的答案,但我认为“当前工作目录”不是想要的。
4赞 Janusz 6/30/2009
您应该补充一点,即使某些文档说 cCurrentpath 可以为 null 并且将由 getcwd 分配,getcwd 似乎也没有在 Mac OS 上分配某些内容并悄悄地使您的程序崩溃
4赞 Lipis 12/20/2009
有一个小错误,但不幸的是我还不能编辑。.第 10 行:cCurrentpath:应为 cCurrentPath
8赞 asveikau 1/28/2010
在 Windows 上的 IMO:通常应避免使用 POSIXy 命名的函数(其中一些以下划线开头)。它们不是真正的 Windows API,而是 CRT。要使用的 Windows API 是 GetCurrentDirectory()。msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
6赞 Lucky Luke 2/15/2014
迈克的回答是正确的。“当前目录”并不总是与运行二进制文件的目录相同。例如,如果应用在 Windows 上作为服务运行,则当前目录可能是 C:\Windows\System32,而二进制目录则不同。
200赞 Mike 10/14/2008 #9

下面是获取执行应用的完整路径的代码: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;

评论

3赞 Frank Szczerba 10/15/2008
我认为这是这里唯一可以回答这个问题的答案,并且适用于 Windows 和 Linux。干得好。
7赞 Chris Lutz 7/4/2009
Boo for /proc/pid/exe - 由于某种原因,OS X 不支持。
30赞 asveikau 1/28/2010
当我看到看到我的一部分的代码时,我有点死了。整个世界都不是 Linux,即使在那个平台上,也应该考虑从版本到版本、架构到架构等的变化。/proc/proc
4赞 Andy Dent 8/4/2010
如果它们在 Linux 上使用别名命令启动,argv[0] 是“命令的名称”还是扩展的?
21赞 charles.cc.hsu 10/5/2016
如何添加,让解决方案更清晰。char pBuf[256]; size_t len = sizeof(pBuf);
3赞 Minok 1/28/2010 #10

只是为了姗姗来迟地堆积在这里,...

没有标准的解决方案,因为这些语言与底层文件系统无关,所以正如其他人所说,基于目录的文件系统的概念超出了 C/C++ 语言的范围。

最重要的是,你想要的不是当前的工作目录,而是运行程序的目录,这必须考虑到程序是如何到达它的位置的——即它是否通过分支作为新进程生成,等等。正如解决方案所演示的那样,要获取运行程序的目录,需要您从相关操作系统的进程控制结构中获取该信息,这是这个问题的唯一权威。因此,根据定义,它是一个特定于操作系统的解决方案。

0赞 mloskot 1/28/2010 #11

正如Minok所提到的,C标准或C++标准中没有指定这样的功能。这被认为是纯粹的特定于操作系统的功能,例如,它在 POSIX 标准中指定。

Thorsten79 给出了很好的建议,它是 Boost.Filesystem 库。但是,如果您不想为程序提供任何二进制形式的链接时间依赖项,这可能会带来不便。

我推荐的一个不错的选择是收集 100% 仅标头的 STLSoft C++ 库 Matthew Wilson(关于 C++ 的必读书籍的作者)。有可移植的外观 PlatformSTL 允许访问特定于系统的 API:WinSTL for Windows 和 UnixSTL on Unix,因此它是可移植的解决方案。所有特定于系统的元素都是使用特征和策略指定的,因此它是可扩展的框架。当然,还提供了文件系统库。

10赞 Adam Yaxley 6/3/2013 #12

在 Windows 上,最简单的方法是使用函数 in 获取指向字符串的指针,该字符串表示可执行文件的绝对路径,包括可执行文件名称。_get_pgmptrstdlib.h

char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe

评论

0赞 aremmell 5/26/2023
如果这是一个编译时常量,如果有人移动了你的程序怎么办?
2赞 Alexey1993 6/11/2013 #13

对于 Windows 系统,您可以在控制台使用 system() 命令。控制台为您提供有关目录等的信息。有关该命令的信息,请参见。但是对于类 Unix 系统,我不知道......如果运行此命令,请读取 bash 命令。 不显示目录...dirdircmdls

例:

int main()
{
    system("dir");
    system("pause"); //this wait for Enter-key-press;
    return 0;
}
47赞 Octopus 10/23/2013 #14

这是来自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 );
}

评论

1赞 Adrian McCarthy 10/25/2013
该 Windows 解决方案不会处理路径中的非 ANSI 字符。您可能应该使用 GetModuleFileNameW 并将其显式转换为 UTF-8(每当您需要发出文件系统命令时,请小心将其转换回来)。
4赞 HelloGoodbye 1/22/2014
对于 Windows 解决方案,我在使用 MinGW 编译时出现错误。error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'
2赞 Octopus 5/8/2014
@Adrian,我通常不是 Windows 程序员,但是没有 DEFINE 或某种方法可以告诉您的编译器自动使用 _W() 风格的函数吗?
1赞 Adrian McCarthy 5/8/2014
@Octopus:要使用宽调用,您需要使用 WCHAR(而不是 char)和 std::wstring(而不是 std::string)。
3赞 freezotic 2/20/2014 #15
#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);
}

评论

0赞 Adrian McCarthy 10/27/2023
该问题没有要求提供当前目录。
1赞 FuzzyQuills 4/21/2015 #16

对于相对路径,这是我所做的。我知道这个问题的年龄,我只想贡献一个更简单的答案,在大多数情况下都有效:

假设你有一条这样的路径:

"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 中)

24赞 Sam Redway 8/15/2015 #17

我知道现在给出答案已经很晚了,但我发现没有一个答案像我自己的解决方案那样有用。获取从 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("\\"));

我认为这应该有效但尚未测试,因此如果它有效或修复,将不胜感激。

评论

0赞 Wodzu 10/20/2016
是的,它也适用于 Windows。我认为这是最好的解决方案。据我所知,argv[0] 始终保留可执行文件的路径。
4赞 Xeverous 4/18/2017
argv[0]这是一个非常好的主意,但不幸的是,我在 Linux 上得到的是“./my_executable_name”或“./make/my_executable_name”。基本上,我得到什么完全取决于我如何启动它
1赞 nilo 3/23/2020
@Xeverous:那又怎样?如果我有一些与我的可执行文件相关的文件需要打开,那么在您的案例中从“./”或“./make/”开始应该可以工作。“.” 是当前的工作目录,argv[0] 会告诉你从那里到可执行文件的相对路径,这正是 OP 应该想要的。无论如何,这正是我所需要的。
20赞 Marqin 1/17/2016 #18

文件系统 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 一起使用,请阅读此内容

评论

13赞 S. Saad 4/10/2017
从引用链接 Downvoted,因为 OP 专门询问可执行文件的当前路径,而不是当前工作目录。1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs.
0赞 Leslie Satenstein 8/22/2016 #19

linux bash 命令,progname 将报告程序的路径。

即使可以从程序中发出 which 命令并将输出定向到 tmp 文件和程序 随后读取该TMP文件,它不会告诉您该程序是否是正在执行的程序。它只告诉您具有该名称的程序所在的位置。

需要的是获取进程 ID 号,并解析出名称的路径

在我的程序中,我想知道该程序是否是 从用户的 bin 目录或路径中的其他目录执行 或从 /usr/bin。/usr/bin 将包含支持的版本。 我的感觉是,在 Linux 中,有一种可移植的解决方案。

1赞 Joachim 10/4/2017 #20

一个库解决方案(尽管我知道这不是要求的)。 如果您碰巧使用Qt:QCoreApplication::applicationDirPath()

3赞 TarmoPikaro 6/10/2019 #21

从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
}

评论

1赞 Not a real meerkat 6/10/2019
不错的答案,但是将声明或定义添加到命名空间 std 是未定义的行为。为了避免这种情况,您可以同时添加命名空间和您选择的第三个命名空间,或者如果您不介意将声明添加到全局命名空间,则直接使用 。std::filesystemstd::experimental::filesystemusing std::filesystem::pathpath
0赞 TarmoPikaro 6/10/2019
我想在 C++14 实验::文件系统不再使用之后,所以你可以忘记这个吗?(进入第一 #if 分行)
4赞 nilo 3/23/2020 #22

只是我的两分钱,但以下代码在 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已经存在很多年了。

评论

0赞 alle_meije 8/25/2021
很干净。很好的答案
2赞 brc-dd 9/6/2021
似乎也部分适用于 Windows(在 PowerShell Core、Windows PowerShell、MinGW 上测试)。从 CMD 运行,这将打印一个空白字符串。
1赞 The Oathman 12/28/2020 #23

当前 .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;

评论

1赞 dxiv 12/28/2020
这复制了另一个先前的答案,并且与该答案不同的是,它将在 ANSI 构建中失败,因为不合格的答案将解析为 而不是 。GetModuleFileNameGetModuleFileNameAGetModuleFileNameW
0赞 The Oathman 12/28/2020
我修改了答案以包括两者。很多爱
-1赞 Tareq Saif 2/27/2022 #24

以下内容在 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