【问题标题】:How do I get the directory that a program is running from?如何获取程序运行的目录?
【发布时间】:2010-09-13 16:16:35
【问题描述】:

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

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

【问题讨论】:

  • 除非您能可靠地从argv[0] 中提取路径,否则该技术将非常依赖于操作系统。
  • 只是为了澄清:“当前目录”,或者,“程序运行的目录”(在问题的术语中)是程序的图像文件所在的目录(〜 .exe 文件),并且“当前工作目录”是该目录,如果程序使用相对路径,则自动完成?
  • 当您#include <windows.h> 时,Windows 会自动将char* 放入_pgmptr 中的可执行路径。如果您只在 Windows 上工作,则不需要调用额外的函数或假设垃圾。
  • 这能回答你原来的问题吗? github.com/gpakosz/whereami
  • 虽然评论来自三年前,但我想扩展 rsethc 关于_pgmptr 的评论。 MSDN 文档指出 _pgmptr_wpgmptr 变量已弃用,您应该改用函数 _get_pgmptr(char**)_get_wpgmptr(wchar_t**)MSDN

标签: c++ c working-directory


【解决方案1】:

获取执行应用的完整路径的代码如下:

窗户:

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;

【讨论】:

  • 我认为这是这里唯一回答这个问题的答案,并且适用于 Windows 和 Linux。干得好。
  • Boo for /proc/pid/exe - 由于某种原因在 OS X 上不受支持。
  • 当我看到/proc 的代码时,我的一部分会死掉一点。整个世界都不是 Linux,即使在那个平台上/proc 也应该被认为会随着版本的变化、arch 的变化等而变化。
  • 如果他们在 Linux 上使用别名命令启动,argv[0] 是“命令名称”还是扩展?
  • 添加char pBuf[256]; size_t len = sizeof(pBuf);如何让解决方案更清晰。
【解决方案2】:

如果您在程序第一次启动时获取当前目录,那么您实际上拥有了启动程序的目录。将值存储在变量中,稍后在程序中引用它。这与the directory that holds the current executable program file 不同。它不一定是同一个目录;如果有人从命令提示符运行程序,那么程序是从命令提示符的当前工作目录运行,即使程序文件位于其他位置。

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

【讨论】:

  • 很好的答案,但我认为“当前工作目录”不是我们想要的。
  • 您应该补充一点,即使某些文档说 cCurrentpath 可以为 null 并且将由 getcwd 分配 getcwd 在 Mac OS 上似乎没有分配任何东西并悄悄地使您的程序崩溃
  • 有一个小错误,但不幸的是我还不能编辑.. line 10: cCurrentpath: should be cCurrentPath
  • IMO 在 Windows 上通常应避免使用以 POSIXy 命名的函数(其中一些以下划线开头)。它们不是真正的 Windows API,而是 CRT。您要使用的 Windows API 是 GetCurrentDirectory()。 msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
  • 迈克的回答是正确的。 “当前目录”并不总是与运行二进制文件的目录相同。例如,如果应用程序在 Windows 上作为服务运行,则当前目录可能是 C:\Windows\System32,而二进制目录则不同。
【解决方案3】:

这是来自cplusplus forum

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

【讨论】:

  • Windows 解决方案不会处理路径中的非 ANSI 字符。您可能应该使用 GetModuleFileNameW 并将其显式转换为 UTF-8(在需要发出文件系统命令时小心地将其转换回来)。
  • 对于 Windows 解决方案,使用 MinGW 编译时出现错误 error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'
  • @Adrian,我通常不是 Windows 程序员,但没有 DEFINE 或某种方式告诉您的编译器自动使用 _W() 风格的函数吗?
  • @Octopus:要使用宽调用,您需要使用 WCHAR(而不是 char)和 std::wstring(而不是 std::string)。
【解决方案4】:

如果您想要一种没有库的标准方式:不。目录的整个概念不包含在标准中。

如果您同意对接近标准库的某些(可移植)依赖是可以的:使用Boost's filesystem library 并请求initial_path()

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

【讨论】:

  • 来自 Boost 文档:模板 const Path& initial_path();返回: 进入 main() 时的 current_path()。并且 current_path() 是“好像通过 POSIX getcwd()”。这不是提问者所要求的。
  • 正如评论的那样,这给出了调用二进制文件的路径,而不是二进制文件的路径......因为它可以从不同的文件夹启动。
【解决方案5】:

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

我认为这应该可行,但尚未测试,因此如果可行,请发表评论,如果不可行,请予以修复。

【讨论】:

  • 是的,它也适用于 Windows。我认为这是最好的解决方案。据我所知,argv[0] 总是保留可执行文件的路径。
  • argv[0] 是一个非常好的主意,但不幸的是我在 Linux 上得到的是“./my_executable_name”或“./make/my_executable_name”。基本上我得到的完全取决于我如何启动它
  • @Xeverous:那又怎样?如果我有一些与我的可执行文件相关的文件需要打开,那么在您的情况下从“./”或“./make/”开始应该可以工作。 “。”是当前工作目录,并且 argv[0] 会告诉你从那里到可执行文件的相对路径,这正是 OP 应该想要的。无论如何,这正是我所需要的。
【解决方案6】:

文件系统 TS is now a standard (并且支持 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 中使用 Filesystem,那么read this

【讨论】:

  • 来自引用的链接,1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. Downvoted,因为 OP 专门询问可执行文件的当前路径而不是当前工作目录。
【解决方案7】:

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

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

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

【讨论】:

    【解决方案8】:

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

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

    【讨论】:

      【解决方案9】:

      也许将当前工作目录与 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] 中是否给出了绝对路径。但更重要的是,如果图像是通过 PATH 定位的呢? linux 是填写完整路径还是只填写命令行内容?
      • 正如 Mike B 所指出的,这是一个非通用的解决方案;它仅在一些非常有限的情况下有效。基本上,只有当您通过相对路径名运行命令时 - 当您运行 ../../../bin/progname 而不是 ./test 时,它并不是那么优雅
      • 如果你解析了 argv[0] 相对于当前目录的可能相对路径(因为 argv[0] 可能是“../../myprogram.exe”),这可能是最安全的方法回答问题。它始终可以工作并且是可移植的(它甚至可以在 Android 上工作!)。
      【解决方案10】:

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

      【讨论】:

      • big catch with this function多线程应用程序和共享库代码不应使用GetCurrentDirectory 函数,应避免使用相对路径名。如果您可以使用此假设,那将是最好的解决方案。
      【解决方案11】:

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

      另外请注意,当前目录和可执行文件所在的目录是两个不同的东西,所以 getcwd() 也帮不了你。

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

      【讨论】:

        【解决方案12】:

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

        【讨论】:

        • 非常干净。很好的答案
        • 似乎也可以在 Windows 上部分工作(在 PowerShell Core、Windows PowerShell、MinGW 上测试)。从 CMD 运行,这将打印一个空白字符串。
        【解决方案13】:

        只是迟来的堆积在这里,...

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

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

        【讨论】:

          【解决方案14】:

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

          例子:

          int main()
          {
              system("dir");
              system("pause"); //this wait for Enter-key-press;
              return 0;
          }
          

          【讨论】:

            【解决方案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);
            }
            

            【讨论】:

              【解决方案16】:

              适用于从 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
              }
              

              【讨论】:

              • 不错的答案,但it is undefined behavior to add declarations or definitions to namespace std。为避免这种情况,您可以将命名空间std::filesystemstd::experimental::filesystem 添加到您选择的第三个命名空间,或者只使用using std::filesystem::path,如果您不介意将path 的声明添加到全局命名空间。
              • 我猜在 C++14 之后实验性::filesystem 不再使用了,所以你可以忘记这个吗? (进入第一个#if 分支)
              【解决方案17】:

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

              假设你有这样的路径:

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

              【讨论】:

                【解决方案18】:

                图书馆解决方案(虽然我知道这不是要求)。 如果你碰巧使用 Qt: QCoreApplication::applicationDirPath()

                【讨论】:

                  【解决方案19】:

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

                  【讨论】:

                  • 这复制了另一个previous answer,并且与那个不同的是,它将在ANSI 构建中失败,因为不合格的GetModuleFileName 将解析为GetModuleFileNameA 而不是GetModuleFileNameW
                  • 我已修改答案以包含两者。很多爱
                  【解决方案20】:

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

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

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

                  【讨论】:

                  • getcwd() 没有按照提问者的要求做。
                  • 是不是接受的答案使用了getcwd(),还是我不理解?
                  • 我投了赞成票,因为你是第一个提出正确答案的人。
                  • 这个答案甚至没有试图解决这个问题。不好意思写了。
                  【解决方案21】:

                  Boost Filesystem 的 initial_path() 的行为与 POSIX 的 getcwd() 类似,而且您自己也不需要,但将 argv[0] 附加到它们中的任何一个都应该可以。

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

                  我之前使用envp 编写了一个解决方案(main() 的第三个参数在 Linux 上工作但在 Windows 上似乎不工作,所以我基本上推荐与其他人之前所做的相同的解决方案,但使用即使结果不漂亮,为什么它实际上是正确的附加解释。

                  【讨论】:

                    【解决方案22】:

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

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

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

                    【讨论】:

                      【解决方案23】:

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

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

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

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

                      【讨论】:

                        【解决方案24】:

                        像这样在stdlib.h 中使用realpath()

                        char *working_dir_path = realpath(".", NULL);
                        

                        【讨论】:

                          【解决方案25】:

                          以下内容在 macOS 10.15.7 上对我来说效果很好

                          brew install boost
                          

                          main.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
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-09-03
                            • 1970-01-01
                            • 1970-01-01
                            • 2012-06-15
                            • 2022-01-08
                            相关资源
                            最近更新 更多