プログラムが実行されているディレクトリを取得するにはどうすればよいですか?


269

C / C ++を使用してプログラムが実行されているディレクトリのフルパスを取得するための、プラットフォームに依存しない方法とファイルシステムに依存しない方法はありますか?現在の作業ディレクトリと混同しないでください。(clibやSTLのような標準的なライブラリでない限り、ライブラリを推奨しないでください。)

(プラットフォーム/ファイルシステムに依存しない方法がない場合、特定のファイルシステムについてWindowsおよびLinuxで機能する提案も歓迎します。)


@chakrit:いいですね。(通常、この問題はWindowsでは発生しません。)
Ashwin Nanjappa

2
からパスを確実に抽出できない限りargv[0]、この手法はOSに大きく依存します。
David R Tribble、2010年

1
明確にするために:(質問の用語で)「現在のディレクトリ」または「プログラムが実行されているディレクトリ」は、プログラムのイメージファイル(〜.exeファイル)が配置されているディレクトリであり、 「現在の作業ディレクトリ」はディレクトリであり、プログラムが相対パスを使用している場合は自動補完されますか?
colemik 2013

3
すると#include <windows.h>、Windowsは自動的にをchar*実行可能パスに配置し_pgmptrます。Windowsのみで作業している場合、追加の関数を呼び出したり、ジャンクであると想定したりする必要はありません。
rsethc 2013年

1
コメントは3年前のものですが、rsethcのについてのコメントをさらに詳しく説明し_pgmptrます。MSDNのドキュメントには、_pgmptrおよび_wpgmptr変数は非推奨であり_get_pgmptr(char**)_get_wpgmptr(wchar_t**)代わりに関数を使用する必要があると記載されています。MSDN
Hydranix 2016年

回答:


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の両方で同じです。良くやった。
フランクSzczerba

6
/ proc / pid / exeのBoo-何らかの理由でOS Xではサポートされていません。
Chris Lutz、

24
私を見るコードを見ると/proc、少し死んでしまいます。すべての世界は、Linuxではなく、一つでもプラットフォームがあることに/procなど、バージョンからバージョンへの変更の対象と見なされるべきで、アーチにアーチ
asveikau

4
Linuxでエイリアスコマンドを使用して起動した場合、argv [0]は「コマンドの名前」ですか、それとも拡張されたものですか。
Andy Dent

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
良い答えですが、「現在の作業ディレクトリ」は必要なものではないと思いました。
マイケルバー

4
一部のドキュメントにcCurrentpathはnullであり、getcwdによって割り当てられると記載されている場合でも、追加する必要があります。getcwdはMac OSで何かを割り当てていないようで、プログラムを静かにクラッシュさせます
Janusz

4
cCurrentPathは次のようになります。そこに小さい誤差があるが、残念ながら私は編集はまだ..ライン10はできません。cCurrentpathは
Lipis

8
WindowsでのIMOは、POSIXyという名前の関数(一部はアンダースコアで始まる)は一般に回避する必要があります。これらは実際のWindows APIではなく、CRTです。使用するWindows APIはGetCurrentDirectory()です。msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
asveikau 2010年

6
マイクの答えは正しいです。「現在のディレクトリ」は、バイナリが実行されているディレクトリと常に同じであるとは限りません。たとえば、アプリがWindowsでサービスとして実行される場合、現在のディレクトリはおそらくC:\ Windows \ System32になりますが、バイナリディレクトリは異なります。
ラッキールーク

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
@エイドリアン、私は通常Windowsプログラマーではありませんが、コンパイラーに関数の_W()フレーバーを自動的に使用するように指示するDEFINEまたはなんらかの方法はありませんか?
タコ

1
@Octopus:ワイドコールを使用するには、WCHAR(charの代わりに)とstd :: wstring(std :: stringの代わりに)を使用する必要があります。
エイドリアンマッカーシー2014年

29

ライブラリなしの標準的な方法が必要な場合:いいえ。ディレクトリの概念全体は標準に含まれていません。

ほぼ標準のlibへの一部の(ポータブル)依存関係に問題がないことに同意する場合:Boostのファイルシステムライブラリを使用して、initial_path()を要求します。

できる限りのカルマを備えた、できる限り近いIMHO(Boostは、定評のある高品質のライブラリセットです)


8
Boost docsから:テンプレート<class Path> const Path&initial_path(); 戻り値:main()へのエントリ時のcurrent_path()。そして、current_path()は、 'POSIX getcwd()のように'です。これは質問者が要求したものではありません。
ジョナサンレフラー

boost 1.4org についてはboost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/…を参照してください
moala

コメントしたように、これは、バイナリへのパスではなく、バイナリが呼び出された場所からのパスを提供します...別のフォルダから開始できるためです。
jpo38 2016年

21

ファイルシステム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を使用する場合は、こちらをお読みください


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は現在の作業ディレクトリではなく、実行可能ファイルの現在のパスについて具体的に尋ねるため、参照されたリンクからDownvoted。
S. Saad 2017

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 2016年

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を手伝うことはできません。

また、現在のディレクトリが必要か、プログラムimage / executableが存在するディレクトリが必要かを明確にする必要があります。現状では、あなたの質問はこの点で少し曖昧です。


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は完全なパスを入力するのでしょうか、それともコマンドラインに何があるのでしょうか?
マイケルバー

Mike Bが指摘したように、これは一般的ではない解決策です。非常に限られた状況でのみ機能します。基本的に、相対パス名でコマンドを実行した場合のみ-./testの代わりに../../../bin/prognameを実行した場合、それほどエレガントではありません
Jonathan Leffler

現在のディレクトリと比較してargv [0]の可能な相対パスを解決する場合(argv [0]は "../../myprogram.exe"である可能性があるため)、おそらくこれが質問に答える最も安全な方法です。これは常に機能し、移植可能です(Androidでも機能します!)。
jpo38 2016年


6

そのためにargv [0]を使用することはできません。通常、実行可能ファイルへのフルパスが含まれていますが、必須ではありません。フィールドに任意の値を指定してプロセスを作成できます。

また、現在のディレクトリと実行可能ファイルがあるディレクトリは2つの異なるものであるため、getcwd()も役立ちません。

WindowsではGetModuleFileName()を使用し、Linuxでは/ dev / proc / procID / ..ファイルを読み取ります。


3

遅ればせながらここに積み上げて...

言語は基礎となるファイルシステムに依存しないため、標準的な解決策はありません。他の人が言ったように、ディレクトリベースのファイルシステムの概念は、c / c ++言語の範囲外です。

その上、現在の作業ディレクトリではなく、プログラムが実行されているディレクトリが必要です。これは、プログラムが現在の場所に到達した方法を考慮に入れる必要があります。ソリューションが示すように、プログラムが実行されているディレクトリを取得するには、問題のオペレーティングシステムのプロセス制御構造からその情報を取得する必要があります。これは、この質問に対する唯一の権限です。したがって、定義により、そのOS固有のソリューションです。


3

コンソールのWindowsシステムでは、system(dir)コマンドを使用できます。また、コンソールにはディレクトリなどに関する情報が表示さdircmdます。コマンドについては、しかし、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

この答えは質問に対処することさえ試みていません。それを書くことに恥。
HelloWorld 2018年

1

相対パスについては、ここで私がやったことです。私はこの質問の年齢を認識しています。ほとんどの場合に機能する単純な答えを投稿したいだけです。

次のようなパスがあるとします。

"path/to/file/folder"

何らかの理由で、Eclipseで作成されたLinuxビルドの実行可能ファイルはこれで正常に動作します。ただし、このようなパスを使用すると、Windowsは非常に混乱します!

上記のように、実行可能ファイルへの現在のパスを取得するにはいくつかの方法がありますが、ほとんどの場合に魅力的な作品を見つける最も簡単な方法は、これをパスのFRONTに追加することです。

"./path/to/file/folder"

"./"を追加するだけでソートできます!:)次に、実行可能ファイル自体がある限り、任意のディレクトリからロードを開始できます。

編集:これが使用されている開発環境である場合にcode :: blocksから実行可能ファイルを起動しようとすると、これは機能しません、何らかの理由で、code :: blocksはものを正しくロードしません...:D

EDIT2:私が見つけたいくつかの新しいことは、コードでこのような静的パスを指定した場合(Example.dataがロードする必要があるものであると仮定):

"resources/Example.data"

その後、実際のディレクトリからアプリを起動した場合(またはWindowsでは、ショートカットを作成し、作業ディレクトリをアプリディレクトリに設定します)、そのように機能します。不足しているリソース/ファイルパスに関連する問題をデバッグするときは、このことを覚えておいてください。(特にIDEからビルドexeを起動するときに間違った作業ディレクトリを設定するIDEで)


1

ライブラリソリューション(これが要求されなかったことは知っていますが)。Qtを使用している場合: QCoreApplication::applicationDirPath()


1

私の2セントだけですが、次のコードは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]);

argv [0]のアイデアを@Sam Redwayに感謝します。そしてもちろん、OPが質問したときにC ++ 17が長年存在しなかったことを理解しています。


0

Boost Filesystem initial_path()はPOSIX getcwd()と同じように動作し、どちらもそれ自体では望みどおりの動作をしませんがargv[0]、どちらかに追加すると動作します。

結果は必ずしもきれいではないことに気づくかもしれません- /foo/bar/../../baz/a.outまたはのようなものを取得するかもしれません/foo/bar//baz/a.outが、それは常に実行可能ファイルに名前を付ける有効なパスをもたらすと信じています(パス内の連続したスラッシュは1つに縮小されることに注意してください)。

私は以前にenvpmain()Linuxでは機能するがWindowsでは機能しないように思われる3番目の引数)を使用してソリューションを記述しました。そのため、以前に他の人が行ったのと同じソリューションをお勧めしますが、それが実際に正しい理由を説明します結果がきれいでなくても。


0

Minokが述べたように、そのような機能はini C標準またはC ++標準で指定されていません。これは純粋にOS固有の機能と見なされ、たとえばPOSIX標準で指定されています。

Thorsten79は良い提案をしてくれました、それはBoost.Filesystemライブラリです。ただし、プログラムでバイナリ形式のリンク時の依存関係を使用したくない場合は、不便な場合があります。

100%ヘッダーのみのSTLSoft C ++ライブラリーである Matthew Wilson(C ++に関する必読の本の著者)のコレクションをお勧めします。ポータブルファサードがあります。PlatformSTLは、システム固有のAPI(WindowsのWinSTLおよびUnixのUnixSTL)へのアクセスを提供するため、ポータブルソリューションです。すべてのシステム固有の要素は、特性とポリシーを使用して指定されるため、拡張可能なフレームワークです。もちろん、ファイルシステムライブラリが提供されています。


0

progname がプログラムへのパスを報告するLinux bashコマンド 。

プログラム内からwhichコマンドを発行してtmpファイルに出力を送信し、その後プログラムがそのtmpファイルを読み取る場合でも、そのプログラムが実行中のプログラムであるかどうかはわかりません。その名前のプログラムがどこにあるかを知らせるだけです。

必要なのは、プロセスID番号を取得し、名前へのパスを解析することです

私のプログラムでは、プログラムがユーザーのbinディレクトリから実行されたか、パスの別のディレクトリから実行されたか、または/ usr / binから実行されたかを知りたいです。/ usr / binには、サポートされているバージョンが含まれます。Linuxでは、移植可能なソリューションが1つあると感じています。


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
}

いい答えですが、宣言または定義を名前空間に追加するのは未定義の動作stdです。これを回避するには、名前空間std::filesystemstd::experimental::filesystem選択した3番目の名前空間の両方を追加するか、グローバル名前空間にusing std::filesystem::path宣言を追加してもかまわない場合は、単にを使用しpathます。
カシオルナン

C ++ 14 experimental :: filesystemはもう使用されていないので、これを忘れることができると思いますか?(最初の#ifブランチに入ります)
TarmoPikaro
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.