Программно получить абсолютный путь к приложению командной строки 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, что я видел в дикой природе.)
7 ответов
Функция _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
захватывает это при инициализации и сохраняет указатель на него. Эта функция использует этот указатель.
Я считаю, что есть гораздо более элегантное решение, которое на самом деле работает для любого 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;
}
Похоже, ответ в том, что вы не можете сделать это:
Я пытаюсь достичь чего-то вроде функциональности lsof и собрать целую кучу статистики и информации о запущенных процессах. Если бы я не был таким медленным, я был бы счастлив придерживаться этого.
Если вы переопределите lsof, вы обнаружите, что он медленный, потому что он выполняет много работы.
Я полагаю, что это не совсем так, потому что lsof работает в пользовательском режиме, скорее, он должен сканировать адресное пространство задачи и искать вещи, поддерживаемые внешним пейджером. Есть ли более быстрый способ сделать это, когда я в ядре?
Нет, это не глупо; он делает то, что должен. Если вы просто хотите получить подмножество его функциональных возможностей, вы можете подумать о том, чтобы начать с исходного кода lsof (который доступен) и урезать его в соответствии с вашими требованиями.
Из любопытства, это
p_textvp
используется вообще? Похоже, он установлен для родителейp_textvp
вkern_fork
(и затем получить освобождение??), но это не касается ни в одном изkern_exec
рутины.
p_textvp
не используется В Дарвине proc не является корнем адресного пространства; задача есть. Не существует понятия "vnode" для адресного пространства задачи, так как оно не обязательно изначально заполняется отображением.Если exec должен заполнить p_textvp, он будет потворствовать предположению, что все процессы поддерживаются vnode. Тогда программисты предполагали бы, что можно получить путь к vnode, и оттуда происходит быстрый переход к предположению, что текущий путь к vnode - это путь, с которого он был запущен, и что обработка текста в строке может привести к имени пакета приложения... все это было бы невозможно гарантировать без существенного штрафа.
Уже поздно, но [[NSBundle mainBundle] executablePath]
прекрасно работает для не связанных программ командной строки.
Думаю, нет гарантированного способа. Если argv[0] является символической ссылкой, то вы можете использовать readlink(). Если команда выполняется через $PATH, можно попробовать некоторые из: search(getenv("PATH")), getenv("_"), dladdr()
http://developer.apple.com/documentation/Carbon/Reference/Process_Manager/Reference/reference.html
GetProcessBundleLocation, кажется, работает.
Почему не просто realpath(argv[0], actualpath);
? Правда, у realpath есть некоторые ограничения (задокументированные на странице руководства), но он прекрасно обрабатывает символические ссылки. Протестировано на FreeBSD и Linux
% ls -l foobar lrwxr-xr-x 1 bortzmeyer bortzmeyer 22 апр. 29 07:39 foobar -> /tmp/get-real-name-exe % ./foobar Мой реальный путь: /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.