Программно получить абсолютный путь к приложению командной строки OS X - PullRequest
20 голосов
/ 29 апреля 2009

В 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, что я видел в дикой природе.)

Ответы [ 7 ]

51 голосов
/ 22 июня 2009

Функция _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 захватывает это при инициализации и сохраняет указатель на него. Эта функция использует этот указатель.

27 голосов
/ 16 ноября 2011

Я считаю, что есть гораздо более элегантное решение, которое на самом деле работает для любого 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;
}
4 голосов
/ 29 апреля 2009

Похоже, ответ в том, что вы не можете сделать это:

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

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

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

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

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

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

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

- Майк Смит, Дарвин Драйверс список рассылки

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

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

1 голос
/ 09 сентября 2013

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

0 голосов
/ 15 мая 2009
0 голосов
/ 29 апреля 2009

Почему бы не просто 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.

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