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


269

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

(如果没有与平台/文件系统无关的方法,那么也欢迎在Windows和Linux中适用于特定文件系统的建议。)


@chakrit:太好了。(尽管在Windows下通常不会出现此问题。)
Ashwin Nanjappa,

2
除非您可以可靠地从中提取路径argv[0],否则该技术将非常依赖于OS。
David R Tribble,2010年

1
需要澄清的是:“当前目录”或“程序从其运行的目录”(在问题的术语中)是程序的图像文件(〜.exe文件)所在的目录,并且“当前工作目录”是目录,如果程序使用相对路径,该目录会自动完成吗?
colemik

3
当您使用#include <windows.h>Windows 时,Windows会自动将A放入char*的可执行路径_pgmptr。如果您仅在Windows上工作,则无需调用额外的函数或假设垃圾。
rsethc

1
尽管此评论来自三年前,但我想扩展rsethc关于的评论_pgmptr。MSDN文档指出_pgmptr_wpgmptr变量已被弃用,您应该使用函数_get_pgmptr(char**)_get_wpgmptr(wchar_t**)代替。MSDN
Hydranix

Answers:


181

这是获取执行应用程序完整路径的代码:

视窗:

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
我认为这是回答问题的唯一答案,对于Windows和Linux都是如此。不错的工作。
Frank Szczerba

6
Boo for / proc / pid / exe-由于某些原因,在OS X上不受支持。
克里斯·卢茨

24
当我看到看起来像/proc我一部分的代码时,就会死掉一点。所有的世界是不是Linux,甚至在一个平台上/proc,应考虑受因版本变化,拱来拱等
asveikau

4
如果它们在Linux上使用别名命令启动,则argv [0]是“命令名称”还是扩展?
安迪·邓特

20
如何添加char pBuf[256]; size_t len = sizeof(pBuf);以使解决方案更清晰。
charles.cc.hsu

166

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

getcwd是POSIX函数,并且所有POSIX兼容平台均支持开箱即用。您不必做任何特殊的事情(除了在Unix上增加正确的标题unistd.h和在Windows上增加Direct.h之外)。

由于您正在创建C程序,它将与默认的c运行时库链接,该库由系统中的所有进程链接到该库(避免了特制的异常),并且默认情况下将包含此函数。CRT从不被视为外部库,因为它为OS提供了基本的标准兼容接口。

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

44
好的答案,但我认为不是“当前工作目录”。
Michael Burr

4
您应该补充一点,即使某些文档说cCurrentpath可以为null并由getcwd分配getcwd在Mac OS上似乎没有分配任何东西,并且使程序安静地崩溃
Janusz 2009年

4
有一个小错误,但不幸的是我还不能编辑..第10行:cCurrentpath:应该是cCurrentPath
Lipis

8
Windows上的IMO通常应避免使用POSIXy命名的功能(某些功能以下划线开头)。它们不是真正的Windows API,而是CRT。您要使用的Windows API是GetCurrentDirectory()。msdn.microsoft.com/zh-CN/library/aa364934(VS.85).aspx
asveikau 2010年

6
迈克的答案是正确的。“当前目录”并不总是与运行二进制文件的目录相同。例如,如果某个应用程序在Windows上作为服务运行,则当前目录可能为C:\ Windows \ System32,而二进制目录则不同。
Lucky Luke

42

这是来自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
该Windows解决方案不会处理路径中的非ANSI字符。您可能应该使用GetModuleFileNameW并将其显式转换为UTF-8(在需要发出文件系统命令时小心将其转换回)。
Adrian McCarthy

3
对于Windows解决方案,error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'使用MinGW编译时出现错误。
HelloGoodbye 2014年

2
@Adrian,我通常不是Windows程序员,但是没有DEFINE或以某种方式告诉您的编译器自动使用_W()函数功能吗?
章鱼

1
@Octopus:要使用广泛调用,您需要使用WCHAR(而不是char)和std :: wstring(而不是std :: string)。
阿德里安·麦卡锡

29

如果要使用没有库的标准方式:否。目录的整个概念未包含在标准中。

如果您同意对标准库的某种(便携式)依赖关系是可以的:使用Boost的文件系统库,并请求initial_path()

恕我直言,您可以通过良好的业力获得最大的收益(Boost是一套完善的高质量库)


8
从Boost文档中:template <class Path> const Path&initial_path(); 返回:进入main()时的current_path()。并且current_path()是“就像通过POSIX getcwd()一样”。这不是发问者的要求。
乔纳森·莱夫勒


如前所述,这提供了二进制文件调用的路径,而不是二进制文件的路径……因为它可以从其他文件夹启动。
jpo38 '16

21

Filesystem 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标志链接。

如果要将Filesystem与Microsoft Visual Studio一起使用,请阅读


6
1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. 引用链接,向下投票,因为OP专门询问可执行文件的当前路径,而不是当前的工作目录。
S. Saad

20

我知道今天要回答这个问题已经很晚了,但是我发现没有一个答案比我自己的解决方案对我有用。从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]始终保留可执行文件的路径。
Wodzu '16

4
argv[0]是一个很好的主意,但不幸的是,我在Linux上得到的是“ ./my_executable_name”或“ ./make/my_executable_name”。基本上我得到的完全取决于我如何启动它
Xeverous

@Xeverous:那又如何?如果我有一些与我的可执行文件有关的文件需要打开,则在您的情况下以“ ./”或“ ./make/”开头应该可以工作。“。” 是当前工作目录,而argv [0]会告诉您从那里到可执行文件的相对路径,这正是OP所需要的。无论如何,这正是我所需要的。
nilo

9

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

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

另外,您还应该澄清是要当前目录还是程序映像/可执行文件所在的目录。就目前情况而言,您的问题在这一点上有点模棱两可。


9

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

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

8

也许用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是填写完整路径还是命令行中的内容?
Michael Burr

正如Mike B所指出的,这是一个非一般性的解决方案。它仅在某些非常有限的情况下有效。基本上,仅当您通过相对路径名运行命令时-当您运行../../../bin/progname而不是./test时,它并不那么优雅
Jonathan Leffler

如果您解析了argv [0]与当前目录相比的可能相对路径(因为argv [0]可能是“ ../../myprogram.exe”),那可能是回答问题的最安全方法。它将始终有效且可移植(甚至可在Android上运行!)。
jpo38 '16


6

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

还请注意,当前目录和带有可执行文件的目录是两件不同的事情,因此getcwd()也不会帮助您。

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


3

只是迟来地堆在这里,...

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

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


3

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

例:

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

2
#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);
}

1

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

在Windows上,您可以使用_getcwd(),因为不建议使用getcwd()

对于标准库,如果Boost对您来说足够标准,我会建议Boost :: filesystem,但它们似乎已从提案中删除了路径标准化。您可能要等到TR2可以立即用于完整的标准解决方案。


10
getcwd()不执行发问者要求的操作。
乔纳森·莱夫勒

是不是接受的答案使用了getcwd(),还是我不仅仅理解?
Sнаđошƒаӽ

我之所以投票,是因为您是第一个提出正确答案的人。
Arnaud 2015年

这个答案甚至都没有尝试解决这个问题。写起来可耻。
HelloWorld '18

1

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

假设您有这样的路径:

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


1

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


1

只有我的两分钱,但是以下代码在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

Boost Filesystem的initial_path()行为类似于POSIX 的行为getcwd(),您自己想要的也不是,但是附加argv[0]到其中任何一个都可以。

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

我以前使用envp(的第三个参数main()在Linux上有效,但在Windows上似乎不可行,所以写了一个解决方案,因此,我本质上推荐的是与以前其他人相同的解决方案,但还附有解释为什么它实际上是正确的即使结果不佳。


0

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

托斯滕79给出了很好的建议,它是Boost.Filesystem库。但是,如果您不希望程序具有二进制形式的任何链接时相关性,这可能会带来不便。

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


0

linux bash命令 哪个progname 将报告程序路径。

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

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

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


0

使用realpath()stdlib.h是这样的:

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

0

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

application.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
}

好的答案,但是将声明或定义添加到namespace的行为是不确定的std。为了避免这种情况,可以将名称空间std::filesystem和名称空间都添加std::experimental::filesystem到您选择的第三个名称空间,或者using std::filesystem::path如果不介意将声明添加path到全局名称空间,则只需使用即可。
卡西奥·雷南

我猜想在C ++ 14实验性::文件系统不再使用之后,您就可以忘记了吗?(进入第一个#if分支)
TarmoPikaro
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.