224

Linux は /proc/self/exe を使えば簡単にできるように思えます。しかし、クロスプラットフォーム インターフェイスを使用して C/C++ で現在のアプリケーションのディレクトリを見つける便利な方法があるかどうか知りたいです。いくつかのプロジェクトが argv[0] をいじっているのを見てきましたが、完全に信頼できるとは思えません。

たとえば、/proc/ を持たない Mac OS X をサポートする必要があるとしたら、どうしますか? #ifdefs を使用して、プラットフォーム固有のコード (NSBundle など) を分離しますか? または、実行可能ファイルのパスを argv[0] や $PATH などから推測しようとして、エッジ ケースでバグを発見する危険を冒していますか?

4

14 に答える 14

377

一部の OS 固有のインターフェース:

この情報を取得するために使用できるサードパーティ ライブラリもあります。たとえば、prideout の回答に記載されている whereami や、Qt を使用している場合は 、コメントに記載されているQCoreApplication::applicationFilePath()などです。

移植可能な (ただし信頼性は低い) メソッドは、 を使用することargv[0]です。呼び出し元プログラムによって任意の値に設定できますが、慣例により、実行可能ファイルのパス名または を使用して検出された名前のいずれかに設定されます$PATH

bash や ksh などの一部のシェルは、実行前に環境変数 " _"を実行可能ファイルのフル パスに設定します。その場合はgetenv("_")、それを取得するために使用できます。ただし、すべてのシェルがこれを行うわけではなく、プログラムを実行する前に変更されなかった親プロセスから何かに設定されたり、残ったりする可能性があるため、これは信頼できません。

于 2009-06-21T22:56:10.410 に答える
25

Gregory Pakosz によるwhereamiライブラリは、mark4oの投稿で言及されている API を使用して、さまざまなプラットフォームにこれを実装します。これは、移植可能なプロジェクトで機能するソリューションが「ただ」必要であり、さまざまなプラットフォームの特性に関心がない場合に最も興味深いものです。

執筆時点でサポートされているプラ​​ットフォームは次のとおりです。

  • ウィンドウズ
  • Linux
  • マック
  • iOS
  • アンドロイド
  • QNX ニュートリノ
  • FreeBSD
  • NetBSD
  • ドラゴンフライBSD
  • SunOS

このライブラリは、 MITwhereami.cおよびwhereami.hWTFPL2 でライセンスされています。ファイルをプロジェクトにドロップし、ヘッダーを含めて使用します。

#include "whereami.h"

int main() {
  int length = wai_getExecutablePath(NULL, 0, NULL);
  char* path = (char*)malloc(length + 1);
  wai_getExecutablePath(path, length, &dirname_length);
  path[length] = '\0';

  printf("My path: %s", path);

  free(path);
  return 0;
}
于 2015-09-26T04:14:10.310 に答える
13

/proc/self/exeLinux でorを使用する代わりにargv[0]、ELF インタープリターによって渡された情報を使用することで、glibc によって次のように利用可能になります。

#include <stdio.h>
#include <sys/auxv.h>

int main(int argc, char **argv)
{
    printf("%s\n", (char *)getauxval(AT_EXECFN));
    return(0);
}

これgetauxvalは glibc 拡張機能であり、堅牢にするためには、返されないようにチェックする必要がありますNULL(ELF インタープリターがAT_EXECFNパラメーターを提供していないことを示します) が、Linux ではこれが実際に問題になることはないと思います。

于 2014-05-13T00:52:20.343 に答える
3

QNX Neutrinoのバージョンに応じて、実行中のプロセスを開始するために使用された実行可能ファイルのフル パスと名前を見つける方法が異なります。プロセス識別子を とし<PID>ます。次のことを試してください。

  1. ファイル/proc/self/exefileが存在する場合、その内容は要求された情報です。
  2. ファイル/proc/<PID>/exefileが存在する場合、その内容は要求された情報です。
  3. ファイル/proc/self/asが存在する場合:
    1. open()ファイル。
    2. 少なくとも のバッファを割り当てsizeof(procfs_debuginfo) + _POSIX_PATH_MAXます。
    3. そのバッファを入力として に渡しますdevctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
    4. バッファを にキャストしprocfs_debuginfo*ます。
    5. 要求された情報は、構造体のpathフィールドにあります。警告: 何らかの理由で、QNXがファイル パスの最初のスラッシュを省略することがあります。必要に応じて前に追加します。procfs_debuginfo//
    6. クリーンアップ (ファイルを閉じる、バッファーを解放するなど)。
  4. 3.ファイルで手順を試してください/proc/<PID>/as
  5. 要求された情報を含む可能性のある構造がdladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)どこにあるかを試してください。dlinfoDl_infodli_fname

これが役立つことを願っています。

于 2015-11-13T15:52:44.083 に答える
2

私の知る限り、そのような方法はありません。また、あいまいさもあります。同じ実行可能ファイルに複数のハードリンクが「指している」場合、何を答えとして取得したいですか? (ハードリンクは実際には「ポイント」しません。ファイル システム階層の別の場所にある同じファイルです。)

新しいバイナリがexecve()正常に実行されると、元のプログラムの引数に関するすべての情報が失われます。

于 2009-06-21T07:14:44.027 に答える
2

mark4o の回答に加えて、FreeBSDには

const char* getprogname(void)

macOS でも利用できるはずです。libbsd を介して GNU/Linux で利用できます。

于 2020-09-11T16:06:03.457 に答える
1

GPL コードを作成し、GNU autotools を使用している場合、多くの OS (Windows や macOS を含む) の詳細を処理する移植可能な方法は、gnulib のrelocatable-progモジュールです。

于 2021-01-30T20:48:09.047 に答える
0

argv[0] を使用して、PATH 環境変数を分析できます。見てください:自分自身を見つけることができるプログラムのサンプル

于 2009-06-21T07:30:51.170 に答える
0

ちょうど私の2セント。このコードを使用すると、クロスプラットフォーム インターフェイスを備えた C/C++ で現在のアプリケーションのディレクトリを見つけることができます。

void getExecutablePath(char ** path, unsigned int * pathLength)
{
    // Early exit when invalid out-parameters are passed
    if (!checkStringOutParameter(path, pathLength))
    {
        return;
    }

#if defined SYSTEM_LINUX

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    // Return written bytes, indicating if memory was sufficient
    int len = readlink("/proc/self/exe", exePath, PATH_MAX);

    if (len <= 0 || len == PATH_MAX) // memory not sufficient or general error occured
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_WINDOWS

    // Preallocate MAX_PATH (e.g., 4095) characters and hope the executable path isn't longer (including null byte)
    char exePath[MAX_PATH];

    // Return written bytes, indicating if memory was sufficient
    unsigned int len = GetModuleFileNameA(GetModuleHandleA(0x0), exePath, MAX_PATH);
    if (len == 0) // memory not sufficient or general error occured
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_SOLARIS

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    // Convert executable path to canonical path, return null pointer on error
    if (realpath(getexecname(), exePath) == 0x0)
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    unsigned int len = strlen(exePath);
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_DARWIN

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    unsigned int len = (unsigned int)PATH_MAX;

    // Obtain executable path to canonical path, return zero on success
    if (_NSGetExecutablePath(exePath, &len) == 0)
    {
        // Convert executable path to canonical path, return null pointer on error
        char * realPath = realpath(exePath, 0x0);

        if (realPath == 0x0)
        {
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        // Copy contents to caller, create caller ownership
        unsigned int len = strlen(realPath);
        copyToStringOutParameter(realPath, len, path, pathLength);

        free(realPath);
    }
    else // len is initialized with the required number of bytes (including zero byte)
    {
        char * intermediatePath = (char *)malloc(sizeof(char) * len);

        // Convert executable path to canonical path, return null pointer on error
        if (_NSGetExecutablePath(intermediatePath, &len) != 0)
        {
            free(intermediatePath);
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        char * realPath = realpath(intermediatePath, 0x0);

        free(intermediatePath);

        // Check if conversion to canonical path succeeded
        if (realPath == 0x0)
        {
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        // Copy contents to caller, create caller ownership
        unsigned int len = strlen(realPath);
        copyToStringOutParameter(realPath, len, path, pathLength);

        free(realPath);
    }

#elif defined SYSTEM_FREEBSD

    // Preallocate characters and hope the executable path isn't longer (including null byte)
    char exePath[2048];

    unsigned int len = 2048;

    int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };

    // Obtain executable path by syscall
    if (sysctl(mib, 4, exePath, &len, 0x0, 0) != 0)
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#else

    // If no OS could be detected ... degrade gracefully
    invalidateStringOutParameter(path, pathLength);

#endif
}

ここで詳しく見ることができます。

于 2020-09-12T06:28:18.997 に答える