簡體   English   中英

如何獲取程序運行的目錄?

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

是否有平台無關和文件系統無關的方法來獲取使用 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 上,最簡單的方法是使用stdlib.h中的_get_pgmptr函數來獲取指向字符串的指針,該字符串表示可執行文件的絕對路徑,包括可執行文件名稱。

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/桌面/./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 已經存在很多年了。

對於控制台上的 Windows 系統,您可以使用 system( dir ) 命令。 控制台為您提供有關目錄等的信息。在cmd閱讀有關dir命令的信息。 但是對於類 Unix 系統,我不知道……如果運行此命令,請閱讀 bash 命令。 ls不顯示目錄...

例子:

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

只是為了遲來的堆積在這里,...

沒有標准解決方案,因為這些語言與底層文件系統無關,所以正如其他人所說,基於目錄的文件系統的概念超出了 c / c++ 語言的范圍。

最重要的是,您想要的不是當前工作目錄,而是程序正在運行的目錄,這必須考慮程序如何到達它的位置 - 即它是否通過 fork 生成為新進程等。如解決方案所示,要獲取程序運行所在的目錄,需要您從相關操作系統的進程控制結構中獲取該信息,這是該問題的唯一權威。 因此,根據定義,它是特定於操作系統的解決方案。

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

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

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

對於標准庫,如果 Boost 對您來說足夠標准,我會建議 Boost::filesystem,但他們似乎已經從提案中刪除了路徑規范化。 您可能必須等到TR2 隨時可用,才能獲得完全標准的解決方案。

Boost Filesystem 的initial_path()的行為類似於 POSIX 的getcwd() ,並且您自己也不需要,但是將argv[0]附加到它們中的任何一個都應該這樣做。

你可能會注意到結果並不總是很漂亮——你可能會得到/foo/bar/../../baz/a.out/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 中有一種可移植的解決方案。

stdlib.h中使用realpath() ,如下所示:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM