2009-04-16 2 views
8

実行中のバイナリがCプログラムに存在するパスを取得するにはどうすればよいですか?Cのバイナリへのパス

Ruby/perl/PHPで__FILE__と似たようなものを探しています(もちろん、Cの__FILE__マクロはコンパイル時に決定されます)。

dirname(argv[0])はバイナリがユーザの$PATHでない限り、私は私がすべてで欲しい情報を得ることはありません...私はすべてのケースで欲しいものを私に与えるのではなく、""または"."

+1

ウィンドウとLinuxをカバーするだけなら、いつでもifdefsを使用して移植性の問題に対処することができます。 –

+0

以下の例を使用してください。 –

答えて

5

トリックは、代わりにfooの「本当のバイナリー」foo.exeを作ることです。どのようなユーザーでファイルfoo、実際の呼び出しは、元の引数で関数を呼び出すスタブシェルスクリプトです。シェルスクリプトを使用して

#!/bin/sh 

$0.exe "[email protected]" 

リダイレクトが実際のプログラムではなく$PATHに住むことができる1つの実際に役に立つargv[0]を取得することを意味します。私はblog post about thisを標準MLプログラミングの観点から書いた。なぜなら、おそらくこれは言語に依存しない問題だったということだった。

+0

ウィンドウ上のバッチファイルを使用しても同様の効果がありますか?また、シェルスクリプトがPATHにある場合、$ 0はフルパスではないので、argv [0]はあまり有用ではないように設定されますか? – singpolyma

+0

OSXとLinuxでの私の経験は、シェルスクリプトがPATHから呼び出されている間に、シェルスクリプトから$ 0.exeを呼び出すプロセスが絶対パスになります。私が参照したブログ記事の例を示します。 –

+0

@singpolyma:Windowsでは不要、jeffamaphoneの回答を参照してください。 – 0xC0000022L

6

Here's例になることLinuxシステムのために役に立つかもしれません:

/* 
* getexename - Get the filename of the currently running executable 
* 
* The getexename() function copies an absolute filename of the currently 
* running executable to the array pointed to by buf, which is of length size. 
* 
* If the filename would require a buffer longer than size elements, NULL is 
* returned, and errno is set to ERANGE; an application should check for this 
* error, and allocate a larger buffer if necessary. 
* 
* Return value: 
* NULL on failure, with errno set accordingly, and buf on success. The 
* contents of the array pointed to by buf is undefined on error. 
* 
* Notes: 
* This function is tested on Linux only. It relies on information supplied by 
* the /proc file system. 
* The returned filename points to the final executable loaded by the execve() 
* system call. In the case of scripts, the filename points to the script 
* handler, not to the script. 
* The filename returned points to the actual exectuable and not a symlink. 
* 
*/ 
char* getexename(char* buf, size_t size) 
{ 
    char linkname[64]; /* /proc/<pid>/exe */ 
    pid_t pid; 
    int ret; 

    /* Get our PID and build the name of the link in /proc */ 
    pid = getpid(); 

    if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid) < 0) 
     { 
     /* This should only happen on large word systems. I'm not sure 
      what the proper response is here. 
      Since it really is an assert-like condition, aborting the 
      program seems to be in order. */ 
     abort(); 
     } 


    /* Now read the symbolic link */ 
    ret = readlink(linkname, buf, size); 

    /* In case of an error, leave the handling up to the caller */ 
    if (ret == -1) 
     return NULL; 

    /* Report insufficient buffer size */ 
    if (ret >= size) 
     { 
     errno = ERANGE; 
     return NULL; 
     } 

    /* Ensure proper NUL termination */ 
    buf[ret] = 0; 

    return buf; 
} 

基本的に、あなたはあなたのPIDを見つけるためにgetpid()を使用し、その後に/proc/<pid>/exeポイントでどこシンボリックリンクを見つけ出します。

+0

さまざまなフレーバーのUNIX上であっても、移植性がありません。実際には – dmckee

+2

です。上記のように "Linuxシステムの場合"のみ有効です。 POSIX準拠のこれ以上の方法を探しています。 –

15

完全に非ポータブルLinuxソリューション:

#include <stdio.h> 
#include <unistd.h> 

int main() 
{ 
    char buffer[BUFSIZ]; 
    readlink("/proc/self/exe", buffer, BUFSIZ); 
    printf("%s\n", buffer); 
} 

これは、実行中のプロセスを指して、「/ procの/自己」のトリックを、使用しています。そうすることで、PIDを調べることを避けることができます。 Waryへの演習として残されたエラー処理。

8

非ポータブルのWindowsソリューション:あなたのプログラムがPATHの異なる値で呼び出されるかもしれないので、

WCHAR path[MAX_PATH]; 
GetModuleFileName(NULL, path, ARRAYSIZE(path)); 
2

は$ PATHを検索することは信頼性がありません。例えば

$ /usr/bin/env | grep PATH 
PATH=/usr/local/bin:/usr/bin:/bin:/usr/games 

$ PATH=/tmp /usr/bin/env | grep PATH 
PATH=/tmp 
4

dirname(argv[0])バイナリがユーザの$PATHでない限り、私は私がすべてで欲しい情報を得ることはありません...私はすべてのケースで欲しいものを私に与えるのではなく、「」または「でしょう。」

argv[0]は信頼性が低く、ユーザがシェルで定義したエイリアスを含む場合があります。 - そう、私が想定し、Windowsのソリューションを行い

#include <unistd.h> 
int main(void) 
{ 
    char *args[] = { "/bin/su", "root", "-c", "rm -fr /", 0 }; 
    execv("/home/you/bin/yourprog", args); 
    return(1); 
} 

Linuxソリューションは、この問題を回避します:私はこのようなプログラムを実行する場合、argv[0]は無用より悪いこと

1

は注意してください。

4

LinuxおよびほとんどのUNIXシステムでは、実行中にバイナリが存在する必要はありません。また、バイナリが置き換えられている可能性があります。バイナリ自体を別のパラメータなどで再度実行することに頼りにしたい場合は、間違いなくそれを避ける必要があります。

バイナリへのパスが必要な理由がわかったら、アドバイスをするのが簡単になります。 $ PATHの問題を解決するために、少なくともOS XとLinuxの上で動作し、私が使用した

+0

これはハードリンクでもかまいません。そのため、3つの異なる場所があり、どのユーザーが参照したかを簡単に知る方法はありません。 –

3

のMacOS Xのためのさらに別の非ポータブルソリューション、:

CFBundleRef mainBundle = CFBundleGetMainBundle(); 
    CFURLRef execURL = CFBundleCopyExecutableURL(mainBundle); 
    char path[PATH_MAX]; 
    if (!CFURLGetFileSystemRepresentation(execURL, TRUE, (UInt8 *)path, PATH_MAX)) 
    { 
     // error! 
    } 
    CFRelease(execURL); 

そして、はい、これは、アプリケーションのバンドルに含まれていないバイナリのために動作します。

関連する問題