在我使用的C++命令行程序中,我需要找到执行路径,即调用.exe文件的路径。例如,如果文件存储为D:storedlocation
,但它是从其他目录(如D:executedhere
(调用的,那么我应该将D:executedhere
作为路径。命令提示符应该看起来有点像这样:-
D:executedhere> D:storedlocationprogram_name.exe getpath
Path of execution is : D:executedhere
我尝试使用GetModuleFileName,正如这里和这里所解释的,但我得到了storedhere
。
有没有一种跨平台的方式来找到";执行路径";?
注意:-我正在使用VSCode。
编辑:编译器版本为:g++(i686-posix-侏儒-rev0,由MinGW-W64项目构建(8.1.0旁注:-由于某些原因,在包含#include<filesystem>
之后,std::filesystem
显示错误:"std::filesystem"尚未声明",所以我不能使用文件系统.h
C++11的答案
出于好奇,我在git bash中安装了MinGW g++(下面是如何在git bash(Windows(中安装gcc(。这立刻奏效了,但第一个坏消息很快就来了:
ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath
$ which g++
/d/MinGW/bin/g++
ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath
$ g++ --version
g++.exe (MinGW.org GCC-6.3.0-1) 6.3.0
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
哦,哎呀g++6.3。这解释了std::filesystem
的OP问题(我也有(。
因此,我修改了另一个答案,将其向后移植到C++11。
我的C++11符合MCVE:
// Declaration (Header):
// standard C++ header:
#include <string>
// returns file path of this executable
std::string getExecPath();
/**************************************************************************/
// Definition (C++ Source):
// standard C++ header:
#include <cstring>
#include <codecvt>
#include <locale>
// OS header:
#ifdef _WIN32 // Is this Windows?
#include <windows.h>
#else // (not) _WIN32 // Then it's hopefully Linux.
#include <unistd.h>
#endif // _WIN32
std::string getExecPath()
{
#ifdef _WIN32 // Is this Windows?
std::wstring path(1024, L' ');
const DWORD len
= GetModuleFileNameW(NULL, &path[0], (DWORD)path.size());
if (!len) return std::string(); // ERROR!
path.resize(len);
std::wstring_convert<std::codecvt_utf8<wchar_t>> convU16ToU8;
return convU16ToU8.to_bytes(path);
#else // (not) _WIN32 // Then it's hopefully Linux.
std::string path(1024, ' ');
ssize_t len
= readlink("/proc/self/exe", &path[0], path.size());
if (len < 0) return std::string(); // ERROR!
path.resize(len);
return path;
#endif // _WIN32
}
std::string getCWD()
{
#ifdef _WIN32 // Is this Windows?
std::wstring path(1024, L' ');
const DWORD len
= GetCurrentDirectoryW(path.size(), &path[0]);
if (!len) return std::string(); // ERROR!
path.resize(len);
std::wstring_convert<std::codecvt_utf8<wchar_t>> convU16ToU8;
return convU16ToU8.to_bytes(path);
#else // (not) _WIN32 // Then it's hopefully Linux.
std::string path(1024, ' ');
if (!getcwd(&path[0], path.size())) return std::string(); // ERROR!
size_t len = std::strlen(path.c_str());
path.resize(len);
return path;
#endif // _WIN32
}
/**************************************************************************/
// Test:
// standard C++ header:
#include <iostream>
int main()
{
std::cout
<< "Exec. Path: " << getExecPath() << 'n'
<< "Current Dir.: " << getCWD() << 'n';
}
MinGW中的测试会话(g++6.3(:
ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath/C++11
$ g++ -std=c++11 -O2 testExecPath.cc && mkdir test ; cd test ; ../a.exe
Exec. Path: D:ds32737EntwicklungtestsC++execPathC++11a.exe
Current Dir.: D:ds32737EntwicklungtestsC++execPathC++11test
ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath/C++11/test
$
对coliru的测试:
g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && mkdir test ; cd test ; ../a.out
Exec. Path: /tmp/1609678428-623235554/a.out
Current Dir.: /tmp/1609678428-623235554/test
注意:
我没有意识到这一点,因为我以前从未在MinGW中使用过g++:_WIN32
也在MinGW内定义,并且使用(并且必须使用(相应的代码
(之前,我使用了_MSC_VER
,它仅为MSVC激活Windows代码。在这种情况下,由于readlink()
不可用,我得到了一个编译器错误。即使是这样,它也可能没有在Linux上做它应该做的事情。(
C++17的答案
MinGW目前似乎与g++6.3一起发货,我认为这是相当旧的。所以,我为C++11写了另一个答案。
顺便说一句,几天前,我在
SO的帮助下解决了同样的问题:获取可执行文件
的路径。
我已将相关部分放入MCVE:
// Declaration (Header):
// standard C++ header:
#include <filesystem>
// returns file path of this executable
std::filesystem::path getExecPath();
/**************************************************************************/
// Definition (C++ Source):
// standard C++ header:
#include <string>
// OS header:
#ifdef _MSC_VER // Is this MSVC?
#include <windows.h>
#else // (not) _MSC_VER // Then it's hopefully Linux/g++.
#include <unistd.h>
#endif // _MSC_VER
std::filesystem::path getExecPath()
{
#ifdef _MSC_VER // Is this MSVC?
std::wstring path(1024, L' ');
const DWORD len
= GetModuleFileNameW(NULL, &path[0], (DWORD)path.size());
if (!len) return std::filesystem::path(); // ERROR!
path.resize(len);
return std::filesystem::path(path);
#else // (not) _MSC_VER // Then it's hopefully Linux/g++.
std::string path(1024, ' ');
ssize_t len
= readlink("/proc/self/exe", &path[0], path.size());
if (len < 0) return std::filesystem::path(); // ERROR!
path.resize(len);
return path;
#endif // _MSC_VER
}
/**************************************************************************/
// Test:
// standard C++ header:
#include <iostream>
int main()
{
std::cout
<< "Exec. Path: " << getExecPath() << 'n'
<< "Current Dir.: " << std::filesystem::current_path() << 'n';
}
首先,我尝试了coliru:
g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && mkdir test ; cd test ; ../a.out
Exec. Path: "/tmp/1609670485.1182494/a.out"
Current Dir.: "/tmp/1609670485.1182494/test"
注:coliru上的g++
版本为g++10.2.0(在撰写本文时(。
我添加了一个CMakeLists.txt
来在我的本地盒子上测试它:
project(ExecPath)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if (UNIX)
# std::filesystem which was added in C++17
# seems to need an extra lib. in g++.
# This might be version dependent...
link_libraries(-lstdc++fs)
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
add_executable(testExecPath testExecPath.cc)
MCVE在Visual Studion 2019中构建并运行。
输出:
Exec. Path: "D:\ds32737\Entwicklung\tests\C++\execPath\build-VS2019\bin\Debug\testExecPath.exe"
Current Dir.: "D:\ds32737\Entwicklung\tests\C++\execPath\build-VS2019"
最后,我在Debian Linux上测试了这一点(在一个带有g++8.3.0的VM中(
测试环节:
ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath$ mkdir build-debian
ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath$ cd build-debian/
ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ cmake ..
-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/hostd/Entwicklung/tests/C++/execPath/build-debian
ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ cmake --build .
Scanning dependencies of target testExecPath
[ 50%] Building CXX object CMakeFiles/testExecPath.dir/testExecPath.cc.o
[100%] Linking CXX executable bin/testExecPath
[100%] Built target testExecPath
ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ bin/testExecPath
Exec. Path: "/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian/bin/testExecPath"
Current Dir.: "/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian"
ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$
g++ 8.3
是我最近安装的Debian的默认值。它有点老了。
因此,我不得不添加-lstdc++fs
来解决std::filesystem
的链接问题。请注意,对于我在coliru中使用的g++ 10.2
,这是不必要的。
请注意,在任何情况下,我都注意到当前工作目录不是可执行文件所在的目录。
为了解决这个任务,我考虑了编码问题。(过去,我经常遇到文件系统和编码问题,只要文件路径中出现ASCII字符以外的所有字符。(
因此,我将GetModuleHandleW()
用于Windows实现,该实现返回UTF-16中的文件路径。
在Linux上,我假设总是使用UTF-8。
严格地说,std::filesystem::path
甚至不是真正必要的。相反,该路径可以被返回,例如作为std::string
。为此,Windows impl。可以将UTF-16转换为UTF-8,以便在任何平台上提供授权编码。