以编程方式检索 OS X 命令行应用程序的绝对路径

问题描述 投票:0回答:8

在 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,这是我在野外看到的。)

c macos unix
8个回答
63
投票

函数

_NSGetExecutablePath
将返回可执行文件的完整路径(GUI 或非 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);

这个函数的秘密在于,Darwin 内核在创建进程时将可执行路径放在进程堆栈上的

envp
数组之后。 动态链接编辑器
dyld
在初始化时获取它并保留指向它的指针。 该函数使用该指针。


36
投票

我相信有更优雅的解决方案,它实际上适用于任何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
投票

看起来答案是你做不到:

我正在努力实现类似的目标 lsof 的功能并收集 一大堆统计数据和信息 关于正在运行的进程。如果 lsof 没那么慢,我很乐意坚持 有了它。

如果你重新实现lsof,你会发现 它很慢,因为它正在做 很多工作。

我想这并不是真的因为 lsof 是用户模式,它更多的是它必须的 扫描任务的地址空间 寻找有支持的事物 外部寻呼机。有没有更快一点的 当我在的时候这样做的方式 内核?

不。 lsof 并不愚蠢;它正在做 它必须做什么。 如果你只是想要一个 其功能的子集,您可能 想要考虑从 lsof 源(可用)和 修剪它以满足您的 要求。

出于好奇,

p_textvp
用于 全部?看起来它被设置为 父母的
p_textvp
kern_fork
中(并且 然后被释放??)但事实并非如此 触摸任何
kern_exec
例行公事。

不使用

p_textvp
。 在达尔文, proc 不是地址的根 空间;任务是。 没有 任务的“vnode”的概念 地址空间,因为它不是 最初必然由 映射一个。

如果 exec 要填充 p_textvp,它 会迎合这样的假设 所有进程都由 vnode 支持。 那么程序员会假设它 有可能找到一条通往 vnode,从那里开始是一个短 跳转到假设 vnode的当前路径是路径 它是从那里启动的,并且 对字符串的文本处理可能 导致应用程序包名称... 所有这些都是不可能的 保证无巨额罚金。

迈克·史密斯,达尔文车手 邮件列表


3
投票

这已经晚了,但是

[[NSBundle mainBundle] executablePath]
对于非捆绑的命令行程序来说效果很好。


2
投票

我认为没有一种有保证的方法。 如果 argv[0] 是符号链接,那么您可以使用 readlink()。 如果命令是通过 $PATH 执行的,那么可以 尝试一些:search(getenv("PATH"))、getenv("_")、dladdr()


0
投票

为什么不简单地

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的解决方案。



0
投票

如果有人正在寻找问题中提到的所有 UNIX 或类 UNIX 操作系统的跨平台解决方案:macOS、Linux、FreeBSD 以及其他一些操作系统。

dladdr(3)
可以通过询问
main
在哪里来提供此信息。

#define _GNU_SOURCE // ask for dladdr under Linux/glibc
#include <dlfcn.h> // dladdr, ..
#include <stdlib.h>
#include <stdio.h>

int main()
{
    Dl_info info;
    if (dladdr(&main, &info) == 0) {
        fprintf(stderr, "error: dladdr: %s\n", dlerror());
        exit(1);
    }
    printf("%s\n", info.dli_fname);
}

如果相关代码无法直接向

main
获取地址,则
dladdr(3)
可以询问其地址。

#define _GNU_SOURCE // ask for dladdr under Linux/glibc
#include <dlfcn.h> // dladdr, ..
#include <stdlib.h>
#include <stdio.h>

int main()
{
    void* pmain = dlsym(RTLD_DEFAULT, "main");
    if (!pmain) {
        fprintf(stderr, "error: dlsym: %s\n", dlerror());
        exit(1);
    }

    Dl_info info;
    if (dladdr(pmain, &info) == 0) {
        fprintf(stderr, "error: dladdr: %s\n", dlerror());
        exit(1);
    }
    printf("%s\n", info.dli_fname);
}
© www.soinside.com 2019 - 2024. All rights reserved.