Путь к бинарному в C - PullRequest
       10

Путь к бинарному в C

9 голосов
/ 17 апреля 2009

Как я могу получить путь, где исполняемый двоичный файл находится в программе на C?

Я ищу что-то похожее на __FILE__ в ruby ​​/ perl / PHP (но, конечно, макрос __FILE__ в C определяется во время компиляции).

dirname(argv[0]) даст мне то, что я хочу во всех случаях, если бинарный файл не находится в $PATH пользователя ... тогда я не получаю информацию, которую хочу вообще, а "" или "."

Ответы [ 9 ]

16 голосов
/ 17 апреля 2009

Абсолютно непереносимое решение Linux:

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

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

Здесь используется трюк "/ proc / self", который указывает на запущенный процесс. Таким образом, это избавляет от необходимости искать PID. Обработка ошибок оставлена ​​как упражнение для осторожного.

8 голосов
/ 17 апреля 2009

Непереносимое решение Windows:

WCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, ARRAYSIZE(path));
6 голосов
/ 17 апреля 2009

Вот пример, который может быть полезен для систем 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;
}

По сути, вы используете getpid(), чтобы найти свой PID, а затем выясните, куда указывает символическая ссылка в /proc/<pid>/exe.

5 голосов
/ 08 мая 2011

Уловка, которую я использовал, которая работает как минимум на OS X и Linux для решения проблемы $ PATH, состоит в том, чтобы сделать «настоящий двоичный файл» foo.exe вместо foo: файл foo, это то, что пользователь фактически вызывает, это скрипт-заглушка, который вызывает функцию с ее исходными аргументами.

#!/bin/sh

$0.exe "$@"

Перенаправление через скрипт оболочки означает, что настоящая программа получает argv[0], которая на самом деле полезна вместо той, которая может жить в $PATH. Я написал в своем блоге сообщение об этом с точки зрения программирования Standard ML, прежде чем мне пришло в голову, что это, вероятно, проблема, не зависящая от языка.

4 голосов
/ 17 апреля 2009

Обратите внимание, что в Linux и большинстве систем UNIX ваш двоичный файл не обязательно должен больше существовать, пока он еще работает. Кроме того, двоичный файл мог быть заменен. Поэтому, если вы хотите снова рассчитывать на выполнение самого бинарного файла с другими параметрами или чем-то еще, вам определенно следует избегать этого.

Было бы легче дать совет, если бы вы сказали, зачем вам нужен путь к самому двоичному файлу?

4 голосов
/ 17 апреля 2009

dirname(argv[0]) даст мне то, что я хочу во всех случаях, если только двоичный файл не находится в пользовательском $PATH ... тогда я не получаю информацию, которую хочу, а скорее "" или "."

argv[0] не является надежным, он может содержать псевдоним, определенный пользователем через его или ее оболочку.

3 голосов
/ 02 ноября 2013

Еще одно непереносимое решение для MacOS X:

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

И, да, это также работает для двоичных файлов, которые не входят в комплекты приложений.

2 голосов
/ 17 апреля 2009

Поиск в $ PATH ненадежен, поскольку ваша программа может вызываться с другим значением PATH. например,

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

$ PATH=/tmp /usr/bin/env | grep PATH
PATH=/tmp
1 голос
/ 17 апреля 2009

Обратите внимание: если я запускаю такую ​​программу, argv[0] хуже бесполезной:

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

Решение Linux работает вокруг этой проблемы, так что, я полагаю, решение для Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...