C/C++を使用してプログラムが実行されている場所からディレクトリのフルパスを取得するための、プラットフォームに依存しない方法とファイルシステムに依存しない方法はありますか。現在の作業ディレクトリと混同しないでください。 (ライブラリがclibやSTLのような標準ライブラリでない限り、ライブラリを推奨しないでください。)
(プラットフォームやファイルシステムにとらわれない方法がない場合は、WindowsやLinuxで特定のファイルシステムに対応する提案も歓迎します。)
実行中のアプリケーションへのフルパスを取得するためのコードは次のとおりです。
Windows:
int bytes = GetModuleFileName(NULL, pBuf, len);
if(bytes == 0)
return -1;
else
return bytes;
Linux:
char szTmp[32];
sprintf(szTmp, "/proc/%d/exe", getpid());
int bytes = MIN(readlink(szTmp, pBuf, len), len - 1);
if(bytes >= 0)
pBuf[bytes] = '\0';
return bytes;
プログラムの最初の起動時に現在のディレクトリを取得すると、プログラムの起動元のディレクトリが事実上得られます。値を変数に保存し、後でプログラムで参照します。これは 現在の実行可能プログラムファイルを保持するディレクトリ とは異なります。必ずしも同じディレクトリではありません。誰かがコマンドプロンプトからプログラムを実行する場合、プログラムは、プログラムファイルが他の場所に存在していても、コマンドプロンプトの現在の作業ディレクトリからrun(である).
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);
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 );
}
ライブラリのない標準的な方法が必要な場合は、次のようにします。いいえ。ディレクトリの概念全体は標準には含まれていません。
標準に近いライブラリへの(移植可能な)依存関係が問題ないことに同意するなら: Boostのファイルシステムライブラリ を使って、 initial_path()を求めてください .
私たちはあなたが得ることができるのと同じくらい近くに、良いカルマを持っています(Boostは定評のある高品質のライブラリです)。
ファイルシステム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
フラグでリンクしてください。
Microsoft Visual StudioでFilesystemを使いたい場合は、 これを読んでください 。
私はこの日に答えを投げることがその日のうちに非常に遅いのを知っています、しかし、私は答えのどれも私自身の解決策として私にとって有益ではなかったことがわかりました。 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("\\"));
私はこれはうまくいくはずだがテストされていないので、それがうまくいくならコメント、またはうまくいかないなら修正が評価されるだろう。
いいえ、標準的な方法はありません。私はC/C++標準がディレクトリ(または他のファイルシステム組織)の存在さえ考慮しないと信じます。
Windowsでは、GetModuleFileName()は、hModuleの場合、現在のプロセスの実行ファイルへのフルパスを返しますパラメータはNULLに設定されます。私はLinuxを手伝うことができません。
また、現在のディレクトリが必要か、プログラムの実行可能ファイルが存在するディレクトリが必要かを明確にしてください。現状では、この点に関してあなたの質問は少しあいまいです。
現在の作業ディレクトリと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:〜/デスクトップ$ ./test
/home/jeremy/Desktop /./ test
そのためにargv [0]を使用することはできません。通常、実行可能ファイルへのフルパスが含まれていますが、必須ではありません。フィールドに任意の値を指定してプロセスを作成できます。
また、現在のディレクトリと実行可能ファイルのあるディレクトリは2つの点で異なるので、getcwd()も役に立ちません。
WindowsではGetModuleFileName()を使用し、Linuxでは/ dev/proc /procID/ ..ファイルを読みます。
Windowsでは、最も簡単な方法は、実行可能ファイル名を含む実行可能ファイルへの絶対パスを表す文字列へのポインタを取得するために_get_pgmptr
内のstdlib.h
関数を使用することです。
char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe
Win32 の場合、GetCurrentDirectory がうまくいくはずです。
ここにちょっと遅れて重ねるだけで...
言語は基礎となるファイルシステムを認識しないため、標準的な解決策はありません。ディレクトリベースのファイルシステムの概念は、c/c ++言語の範囲外です。
それに加えて、現在の作業ディレクトリではなく、プログラムが実行されているディレクトリが必要です。これは、プログラムがどのようにしてどこに到達したかを考慮する必要があります。ソリューションが示しているように、プログラムが実行されているディレクトリを取得するには、問題のオペレーティングシステムのプロセス制御構造からその情報を取得する必要があります。これが、この問題に対する唯一の権限です。したがって、定義上、そのOS固有のソリューションです。
Windowsシステムのコンソールではsystem(dir
)コマンドを使うことができます。そしてconsoleはあなたにディレクトリなどに関する情報を与えます。dir
でcmd
コマンドについて読んでください。しかし、Unixライクなシステムでは、わかりません...このコマンドが実行されたら、bashコマンドを読んでください。 ls
はディレクトリを表示しません...
例:
int main()
{
system("dir");
system("pause"); //this wait for Enter-key-press;
return 0;
}
#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);
}
Linuxのbashコマンド which progname は、プログラムへのパスを報告します。
プログラム内からwhichコマンドを発行して出力をtmpファイルに送信し、その後プログラムがそのtmpファイルを読み取る場合でも、そのプログラムが実行中のプログラムであるかどうかはわかりません。それはその名前を持つプログラムがどこにあるかをあなたに知らせるだけです。
必要なのはあなたのプロセスID番号を取得し、名前へのパスを解析することです。
私のプログラムでは、プログラムがユーザーのbinディレクトリから実行されたのか、パス内の別のディレクトリから実行されたのか、または/ usr/binから実行されたのかを知りたいと思います。/usr/binはサポートされているバージョンを含みます。私の考えでは、Linuxには移植可能な解決策が1つあります。
相対パスについては、これが私がしたことです。私はこの質問の年齢を知っています、私は単純な答えを単に大多数の場合にうまくいくように貢献したいと思います:
このような道があるとしましょう。
"path/to/file/folder"
何らかの理由で、Eclipseで作られたLinuxで構築された実行ファイルはこれでうまく動作します。ただし、このようなパスを指定すると、Windowsは非常に混乱します。
実行可能ファイルへの現在のパスを取得する方法はいくつかありますが、ほとんどの場合、最も簡単な方法は、これをパスの前面に追加することです。
"./path/to/file/folder"
「./」を追加するだけでソートできます。 :)それで、実行可能ファイル自体であれば、好きなディレクトリからロードを開始できます。
編集:それが使用されている開発環境である場合は、code :: blocksから実行可能ファイルを起動しようとした場合、これは動作しません。
編集2:私が発見したいくつかの新しいことはあなたがあなたのコードでこのような静的パスを指定する場合です(Example.dataがあなたがロードする必要があるものであると仮定して)
"resources/Example.data"
その後、実際のディレクトリからアプリケーションを起動した場合(またはWindowsではショートカットを作成し、作業ディレクトリをあなたのアプリケーションディレクトリに設定した場合)、そのように動作します。不足しているリソース/ファイルパスに関連する問題をデバッグするときは、このことを頭に入れておいてください。 (特にIDEからbuild exeを起動するときに間違った作業ディレクトリを設定したIDEでは)
Minokが述べたように、C標準またはC++標準にはそのような機能は規定されていません。これは純粋にOS特有の機能であると考えられており、例えばPOSIX標準で規定されています。
Thorsten79は良い提案をしています、それはBoost.Filesystemライブラリです。しかし、あなたのプログラムでバイナリ形式のリンク時の依存関係を持ちたくない場合は不便かもしれません。
私がお勧めする良い代替案は100%ヘッダのみのコレクションです STLSoft C++ライブラリMatthew Wilson (C++に関する必読書の著者)。可搬式ファサードがありますPlatformSTLはシステム特有のAPIへのアクセスを提供します:Windows用のWinSTLとUnix上のUnixSTLです、それでそれは移植可能な解決策です。システム固有の要素はすべて、特性とポリシーを使用して指定されているため、拡張可能なフレームワークです。もちろんファイルシステムライブラリも提供されています。
Boost Filesystemのinitial_path()
はPOSIXのgetcwd()
のように振る舞います、そしてあなた自身が望むこともしませんが、どちらかにargv[0]
を追加するべきです。
/foo/bar/../../baz/a.out
や/foo/bar//baz/a.out
のような結果になるかもしれませんが、実行可能ファイルに名前を付ける有効なパスが常に得られると思います(パス内の連続するスラッシュは1つに折りたたまれることに注意してください)。
私は以前、envp
(main()
の3番目の引数で、Linuxでは動作していましたが、Windowsでは動作しないようでした)を使用した解決策を書いています。結果がきれいでなくても実際に正しいです。
POSIXプラットフォームでは、 getcwd() を使用できます。
Windowsでは、 _ getcwd() を使用できます。 getcwd() の使用は推奨されていないためです。
標準ライブラリの場合、Boostがあなたにとって十分に標準的であれば、Boost :: filesystemを提案したはずですが、それらは提案からパス正規化を削除したようです。完全に標準的な解決策のためには、 TR2がすぐに利用可能になる まで待たなければならないかもしれません。
ライブラリソリューション(私はこれが要求されなかったことを知っていますが)。偶然Qt:QCoreApplication::applicationDirPath()
を使うなら
このようにstdlib.h
でrealpath()
を使用します。
char *working_dir_path = realpath(".", NULL);
実験的なファイルシステムを使用して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
}