[英]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;
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.