Программно получить абсолютный путь для приложения командной строки OS X

В Linux приложение может легко получить свой абсолютный путь, запросив /proc/self/exe. На FreeBSD это больше связано с тем, что вам нужно создать вызов sysctl:

int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
char buf[1024];
size_t cb = sizeof(buf);
sysctl(mib, 4, buf, &cb, NULL, 0);

но это все еще вполне выполнимо. Однако я не могу найти способ определить это на OS X для приложения с командной строкой. Если вы работаете из пакета приложений, вы можете определить его, запустив [[NSBundle mainBundle] bundlePath], но поскольку приложения из командной строки не находятся в связках, это не помогает.

(Примечание: консультация argv[0] не является разумным ответом, так как при запуске из символической ссылки argv[0] будет эта символьная ссылка, а не конечный путь к исполняемому вызову. argv[0] также может лежать, если немое приложение использует вызов exec() и забывает инициализировать argv правильно, что я видел в дикой природе.)

Ответы

Ответ 1

Функция _NSGetExecutablePath вернет полный путь к исполняемому файлу (GUI или нет). Путь может содержать символические ссылки ".." и т.д., Но функция realpath может быть используемые для их очистки, если это необходимо. Подробнее см. man 3 dyld.

char path[1024];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0)
    printf("executable path is %s\n", path);
else
    printf("buffer too small; need size %u\n", size);

Секрет этой функции заключается в том, что ядро ​​Дарвина запускает исполняемый путь в стек процесса сразу после массива envp, когда он создает этот процесс. Редактор динамической компоновки dyld захватывает это при инициализации и сохраняет указатель на него. Эта функция использует этот указатель.

Ответ 2

Я считаю, что есть намного более элегантное решение, которое действительно работает для любого PID, а также возвращает абсолютный путь напрямую:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libproc.h>

int main (int argc, char* argv[])
{
    int ret;
    pid_t pid; 
    char pathbuf[PROC_PIDPATHINFO_MAXSIZE];

    pid = getpid();
    ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
    if ( ret <= 0 ) {
        fprintf(stderr, "PID %d: proc_pidpath ();\n", pid);
        fprintf(stderr, "    %s\n", strerror(errno));
    } else {
        printf("proc %d: %s\n", pid, pathbuf);
    }

    return 0;
}

Ответ 3

Похоже, ответ заключается в том, что вы не можете этого сделать:

Я пытаюсь добиться чего-то вроде   lsof и собрать   целая куча статистики и информации   о запущенных процессах. Если lsof   были не такими медленными, я был бы счастлив придерживаться   с ним.

Если вы переопределяете lsof, вы найдете что он медленный, потому что он делает много работы.

Я предполагаю, что на самом деле не потому, что lsof   это пользовательский режим, это больше, что он должен   сканировать через адресное пространство задачи   ищет вещи, поддерживаемые   внешний пейджер. Быстрее ли   способ сделать это, когда я нахожусь в   ядро?

Нет. lsof не является глупым; он делает что он должен делать. Если вы просто хотите подмножество его функциональности, вы можете хочу рассмотреть возможность начать с lsof source (который доступен) и обрезая его, чтобы встретить требования.

Из любопытства, p_textvp используется в   все? Похоже, он настроен на   parent p_textvp в kern_fork (и   затем освобождение?), но это не   попадание в любой из kern_exec  подпрограммы.

p_textvp не используется. В Дарвине proc не является корнем адреса пространство; задача есть. Здесь нет концепция "vnode" для задачи адресное пространство, поскольку оно не обязательно изначально населенных отображение одного.

Если exec должен был заполнить p_textvp, это будет потворствовать предположению, что все процессы поддерживаются vnode. Тогда программисты предположили бы, что это было возможно получить путь к vnode, и оттуда это короткий переходим к предположению, что текущий путь к vnode - это путь из которого он был запущен, и что обработка текста на строке может привести к названию прикладного пакета... все из которых было бы невозможно гарантия без существенного штрафа.

Майк Смит, Darwin Drivers список рассылки

Ответ 4

Невозможно гарантировать, что я думаю. Если argv [0] является символической ссылкой, вы можете использовать readlink(). Если команда выполняется через $PATH, тогда можно попробуйте некоторые из: search (getenv ( "PATH" )), getenv ( "_" ), dladdr()

Ответ 5

Поздно, но [[NSBundle mainBundle] executablePath] отлично работает для несвязанных программ командной строки.

Ответ 6

Почему бы не просто realpath(argv[0], actualpath);? Правда, realpath имеет некоторые ограничения (задокументированные на странице руководства), но отлично справляется с символическими ссылками. Протестировано на FreeBSD и Linux

    % ls -l foobar 
    lrwxr-xr-x  1 bortzmeyer  bortzmeyer  22 Apr 29 07:39 foobar -> /tmp/get-real-name-exe

    % ./foobar 
    My real path: /tmp/get-real-name-exe
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>

int
main(argc, argv)
    int             argc;
    char          **argv;
{
    char            actualpath[PATH_MAX + 1];

    if (argc > 1) {
        fprintf(stderr, "Usage: %s\n", argv[0]);
        exit(1);
    }
    realpath(argv[0], actualpath);
    fprintf(stdout, "My real path: %s\n", actualpath);
    exit(0);
}

Если программа запускается через PATH, см. решение pixelbeat.