How do I find the location of the executable in C?
在C/C++中有没有找到当前执行程序的位置(完整路径)的方法?
(argv[0]的问题是它没有给出完整的路径。)
- 哪个操作系统?
- 我不认为有一种便携式的方法可以做到这一点。如果使用完整静态路径调用程序,argv[0]是否具有完整路径?如果是这样,您可以像sshd那样强制用户执行二进制文件。
- 这里也有很好的答案:stackoverflow.com/questions/1023306/…
- 这个问题("如何在设置x中找到程序的位置?")真的可以使用标签;很难用关键字搜索!
- argv[0]的另一个问题是,如果您试图在库中执行此操作,那么它将不可用。
- 副本中的最上面的答案比这里的任何或所有答案都更全面,尽管这是一个最近的问题。因此,我把这个作为它的一个复制品来结束,而不是相反,即使这是一个老问题,它通常会作为主题的主问题得到点头。
总结:
在与/proc的unixes上,真正直接和可实现的方法是:
readlink("/proc/self/exe", buf, bufsize)(Linux)
readlink("/proc/curproc/file", buf, bufsize)(自由标准)
readlink("/proc/self/path/a.out", buf, bufsize)(Solaris)
在不带/proc的unixes上(即,如果上述失败):
如果argv[0]以"/"(绝对路径)开头,则这是路径。
否则,如果argv[0]包含"/"(相对路径),则将其附加到cwd(假设还没有改变)。
否则,在$PATH中搜索目录以查找可执行的argv[0]。
然后检查可执行文件是否实际上不是符号链接可能是合理的。如果它是相对于symlink目录解析的。
在/proc方法中不需要此步骤(至少对于Linux)。这里proc symlink直接指向可执行文件。
注意,正确设置argv[0]取决于调用进程。大多数时候都是正确的,但是有时调用进程不能被信任(例如setuid可执行文件)。
在Windows上:使用GetModuleFileName(NULL, buf, bufsize)。
- +1个尖锐的回答
- 任何依赖argv[0]作为程序名的内容都是不可靠的。它在大多数时候都会起作用,但不是每次都起作用。这个问题在没有/proc的unixes上很难解决。
- 不是所有带proc的unix都有/proc/self/exe。/proc的布局完全是操作系统特定的,它们都有点不同。例如,freebsd提供了/proc/curproc/file,其工作方式与Linux的/proc/self/exe相同。但其他人可能根本不会这么做。
- 注意,如果有人需要隐藏他们的跟踪,那么execl("/home/hacker/.hidden/malicious","/bin/ls","-s", (char *)0);会给argv[0]留下一个绝对路径名,与执行的文件名无关。不过,其他信息是有用的;谢谢。
- @当然,要正确设置argv[0]是由生成过程决定的,调用的程序很容易被填满。然而,你的例子很奇怪。误导恶意程序有什么意义?更好的例子是setuid可执行文件。在这种类型的代码中,argv[0]是不可信任的。
- @Lispmachine:我担心的恶意程序并不是那么容易误导;而是误导恶意程序使用的库-库试图找出哪个程序使用它将信息传递给DBMS。除非您有类似于/proc/self/exe的东西,否则我看不出库如何可靠地计算出哪个程序正在执行它。它现在引起了严重的头痛。我对"识别可执行文件"问题的AIX和HP-UX解决方案也很感兴趣。(Linux、MacOS X、Solaris、Windows似乎不是主要问题。)
- 如果您不使用/proc(可能是因为它是系统特定的),在实践中,您经常会陷入案例3中,您正在搜索路径。不幸的是,这是不可靠的:人们可以(而且我经常这样做)用不同于当前shell的路径运行程序,这样寻找自己的程序很可能得到错误的答案。正如其他人指出的那样,这些方法(除了/proc方法)都不能可靠地工作。
- @davepacheco在shell中更改$path根本不是问题,因为生成的进程将从shell继承env变量。当调用进程没有将argv[0]设置为可执行文件的名称/路径时,问题就会出现。
- @库中的jonathanlefler代码与使用它的程序代码具有相同的特权。据我所知,在操作系统级别上唯一被普遍接受的特权分离方法是运行单独的进程。
- @lispmachine见第(3)条。程序可以使用与用于查找子可执行文件的父环境不同的任意环境来执行子进程。你说得对,这更像是一个拐弯的情况,因为如果有人运行"path=",情况就不会发生。我的命令"在外壳上。
- @戴夫帕切科,你显然会把孩子的环境搞得一团糟,但你为什么要这样做?
- 一个模糊的角落案例:如果exe驻留在一个ClearCase MVFS视图中,那么linux/proc/self/exe方法就不太管用,因为linux最终返回的是视图存储目录中可执行文件的位置,而不是/view限定的路径。例如,对于/vbs/bldsupp/linuxammd64/clang/debug/bin/llvm config/proc/self/exe,将我指向不友好的路径:/home/peeterj/views/peeterj-7.vws/.s/00024/800023250b‌&8203;8f17fllvm-tblgen
- 在Windows上,您不必调用GetModuleFileName。相反,只需#include 并使用_pgmptr中Windows自动提供的路径字符串。它比使用GetModuleFileName函数更容易,因为它有失败的可能性。
- @Jonathan我对AIX和HP-UX也很感兴趣,这是我们的软件所支持的唯一一个使用argv[0]回退的平台。你有没有找到解决办法?
- @Nicholaswilson:我们确实找到了针对HP-UX和AIX的解决方案,这些解决方案不依赖于对argv[0]的访问,但我现在不记得函数调用是什么(除了"不可移植")。我再也不能直接访问代码了,所以我查不到我们做了什么。完成O/S手册的时间-第2节和/或第3节。
如果使用的是Windows,请使用getModuleFileName()函数。
请注意,以下注释仅适用于Unix。
对这个问题学究式的回答是,在所有情况下都没有一般的正确回答这个问题的方法。正如您所发现的,argv[0]可以由父进程设置为任何内容,因此不需要与程序的实际名称或其在文件系统中的位置有任何关系。
但是,以下启发式方法通常有效:
如果argv[0]是绝对路径,则假定这是可执行文件的完整路径。
如果argv[0]是相对路径,即它包含一个/,则使用getcwd()确定当前工作目录,然后将argv[0]附加到它。
如果argv[0]是一个普通字,请在$path中搜索argv[0],然后将argv[0]附加到您在其中找到它的任何目录中。
注意,所有这些都可以通过调用相关程序的过程来规避。最后,您可以使用特定于Linux的技术,如EMG-2所提到的。在其他操作系统上可能也有类似的技术。
即使假设上面的步骤为您提供了一个有效的路径名,您仍然可能没有实际想要的路径名(因为我怀疑您实际想要做的是在某个地方找到一个配置文件)。存在硬链接意味着您可以有以下情况:
1 2 3 4
| -- assume /app/bin/foo is the actual program
$ mkdir /some/where/else
$ ln /app/bin/foo /some/where/else/foo # create a hard link to foo
$ /some/where/else/foo |
现在,上面的方法(包括/proc/$pid/exe)将使/some/where/else/foo成为程序的真正路径。事实上,这是一条通往程序的真正路径,而不是你想要的路径。注意,这个问题不会发生在符号链接上,符号链接在实践中比硬链接更常见。
尽管这种方法在原则上是不可靠的,但在大多数情况下,它在实践中都能很好地工作。
实际上不是一个答案,只是一个需要记住的注释。
正如我们所看到的,在Linux和Unix中,查找运行可执行文件的位置的问题非常棘手,而且是平台特有的。在这样做之前,应该三思而后行。
如果您需要您的可执行位置来发现某些配置或资源文件,那么您可能应该按照Unix的方式将文件放入系统:将configs放到/etc或/usr/local/etc或当前用户主目录中,而/usr/share是放置资源文件的好地方。
在许多POSIX系统中,您可以检查/proc/pid/exe下的simlink。举几个例子:
1 2 3 4
| # file /proc/*/exe
/proc/1001/exe: symbolic link to /usr/bin/distccd
/proc/1023/exe: symbolic link to /usr/sbin/sendmail.sendmail
/proc/1043/exe: symbolic link to /usr/sbin/crond |
- /proc不是posix,也不是很标准化。许多现代的独木舟都有,有些没有。
- 学习新事物总是好的。谢谢。有没有更多的"程序化"方法来做到这一点?
- @迪特里希:你说得对,这不是POSIX。据维基百科所说,类似Unix的系统有:Linux、AIX、BSD、Solaris、QNX。但是,它没有说明所有这些系统是否都有/proc/*/cmd simlink。
- Solaris没有/proc,但没有/proc/*/cmd。
- @未知(Google):查看man 2:3 readlink或此处:linux.die.net/man/3/readlink
- 在Linux/proc中,文件系统是一个内核级别的选项,因此不能保证它在任何给定的Linux系统上都可用或启用。此外,它可以在内核中启用,但如果/etc/fstab没有安装点,则不可用。此外,您可能会遇到安全问题。
请记住,在UNIX系统上,二进制文件自启动以来可能已被删除。它在Unix上是完全合法和安全的。上次我检查的Windows不允许删除正在运行的二进制文件。
/proc/self/exe仍然是可读的,但实际上它不是一个有效的符号链接。这将是…奇怪的。
- 在运行之前,Unix是否将所有可执行程序加载到内存中?在这种情况下,它如何有足够的内存来运行非常大的程序,比如一些自解压的归档文件
- 通常不会。可执行文件被锁定(试图打开进行写入会导致"文本文件忙")和"内存映射",这意味着它看起来都在内存中,但在第一次访问内存页时,它将被延迟加载。如果它是一个只读页(代码通常是这样),那么内核可以在需要内存时"忘记"数据,并在再次被访问时重新加载数据。有点像交换,但是由于代码没有被修改,它将永远不会被写回磁盘。
- 我想在这种情况下,如果在删除文件后将卸载的内存页加载到内存中,就会发生严重的事情。
- 什么都不会发生。iirc,你不需要"删除"一个文件,只需要删除它在目录中的条目。一旦所有对它的引用消失,实际文件将自动删除。引用可以是文件系统条目(硬链接)或打开的文件描述符(如进程的图像)。因此,在您"删除"文件之后,该文件将对您不可见,但实际删除将延迟到进程终止。
在Mac OS X上,使用_NSGetExecutablePath。
见man 3 dyld和类似问题的答案。
对于Linux,您可以在一个名为binreloc的不错的库中找到/proc/self/exe的方法,该库位于:
- 网址:http://autopackage.org/docs/binreloc/
我愿意
1)使用basename()函数:http://linux.die.net/man/3/basename2)chdir()到该目录3)使用getpwd()获取当前目录
这样,您将得到一个整洁、完整的目录,而不是./或../bin/。
如果这对你的程序很重要的话,也许你想保存并恢复当前目录。