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

时间:2009-04-28 20:31:18

标签: c macos unix

在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]将是该符号链接 - 而不是被称为可执行文件的最终路径。{{1}如果一个愚蠢的应用程序使用argv[0]调用并忘记正确初始化argv,我也会说谎,这是我在野外看到的。)

7 个答案:

答案 0 :(得分:51)

函数_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在初始化时抓取它并保留指向它的指针。该函数使用该指针。

答案 1 :(得分:27)

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

答案 2 :(得分:4)

看起来答案是你不能这样做:

  
    

我正在努力实现类似的目标     lsof的功能和收集     一大堆统计和信息     关于运行流程。如果lsof     不是那么慢,我会很高兴坚持     用它。

  
     

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

     
    

我想这不是因为lsof     是用户模式,它不仅仅是必须的     扫描任务的地址空间     寻找支持的东西     外部寻呼机。有没有更快     我在这里的时候这样做的方式     内核?

  
     

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

     
    

出于好奇,p_textvp用于     所有?它看起来像是设置为     p_textvp中的父级kern_fork(和     然后被释放??)但事实并非如此     感受kern_exec中的任何一个     例程。

  
     

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

     

如果exec要填充p_textvp,那么   会迎合这样的假设   所有进程都由vnode支持。   然后程序员会认为它   有可能找到一条路   vnode,从那里它是一个短暂的   跳到假设   vnode的当前路径是路径   它是从哪个发起的   字符串上的文本处理可能   导致应用程序包名称...   所有这些都是不可能的   保证没有实质性的惩罚。

     

- Mike Smith, Darwin Drivers mailing list

答案 3 :(得分:2)

我认为没有保证。 如果argv [0]是符号链接,那么你可以使用readlink()。 如果通过$ PATH执行命令,则可以执行 尝试一些:搜索(getenv(“PATH”)),getenv(“_”),dladdr()

答案 4 :(得分:1)

这已经很晚了,但[[NSBundle mainBundle] executablePath]适用于非捆绑的命令行程序。

答案 5 :(得分:0)

为什么不简单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的解决方案。

答案 6 :(得分:0)