关于c ++:在没有/ proc / self / exe的情况下查找当前可执行文件的路径

Finding current executable's path without /proc/self/exe

在我看来,Linux使用/proc/self/exe很容易。但我想知道是否有一个方便的方法来找到当前应用程序的目录在C/C++与跨平台接口。我见过一些项目在argv[0]上乱作一团,但它似乎并不完全可靠。

如果你必须支持Mac OS X,它没有/proc/,你会怎么做?使用ifdefs隔离特定于平台的代码(例如nsbundle)?或者尝试从argv[0]、$path和其他什么来推断可执行文件的路径,这样会冒在边缘情况下发现错误的风险?


一些操作系统特定的接口:

  • Mac OS X:_NSGetExecutablePath()(男3岁)
  • Linux:readlink /proc/self/exe
  • Solaris:getexecname()
  • Freebsd:sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
  • freebsd如果有procfs:readlink /proc/curproc/file(freebsd默认没有procfs)
  • NetBSD:readlink /proc/curproc/exe
  • 蜻蜓BSD:readlink /proc/curproc/file
  • windows:GetModuleFileName(),带hModule=NULL

便携式(但不太可靠)方法是使用argv[0]。虽然调用程序可以将其设置为任何内容,但按照惯例,它被设置为可执行文件的路径名或使用$PATH找到的名称。

一些shell,包括bash和ksh,在执行前将环境变量"EDOCX1"(14)设置为可执行文件的完整路径。在这种情况下,您可以使用getenv("_")来获取它。但是,这是不可靠的,因为并非所有的shell都这样做,并且它可以设置为任何内容,或者从父进程中遗留下来,而父进程在执行程序之前没有更改它。


使用/proc/self/exe是不可移植和不可靠的。在我的Ubuntu 12.04系统上,您必须是根用户才能读取/跟踪symlink。这将使Boost示例和可能发布的whereami()解决方案失败。好的。

这篇文章很长,但是讨论了实际的问题,并给出了代码,这些代码实际上与针对测试套件的验证一起工作。好的。

找到程序的最佳方法是追溯系统使用的相同步骤。这是通过使用针对文件系统根目录、pwd、路径环境和考虑符号链接以及路径名规范化的argv[0]解决的。这是记忆造成的,但我过去成功地做到了这一点,并在各种不同的情况下对其进行了测试。它不能保证工作,但是如果不工作,你可能会遇到更大的问题,而且总的来说,它比讨论的任何其他方法都更可靠。在与Unix兼容的系统中,有些情况下,正确地处理argv[0]将无法使您进入程序,但您将在一个明显损坏的环境中执行。从1970年起,它还可以移植到所有的UNIX派生系统,甚至一些非UNIX派生系统,因为它基本上依赖libc()标准功能和标准命令行功能。它应该适用于Linux(所有版本)、Android、Chrome OS、Minix、原始Bell Labs Unix、FreeBSD、NetBSD、OpenBSD、BSD X.X、SunOS、Solaris、SysV、HPUX、Concentrix、SCO、Darwin、AIX、OS X、NextStep等。如果程序是直接从GUI环境启动的,则可能需要稍加修改,例如VMS、VM/CMS、DOS/Windows、ReactOS、OS/2等。当然,它应该把argv[0]设置为绝对路径。好的。

要知道,几乎每个与Unix兼容的操作系统上发布的shell基本上都以相同的方式查找程序,并以几乎相同的方式设置操作环境(带有一些可选的额外功能)。而任何其他启动程序的程序都应该为该程序创建相同的环境(argv、环境字符串等),就像它是从shell运行的一样,带有一些可选的额外功能。程序或用户可以为其启动的其他下级程序设置一个偏离此约定的环境,但如果这样做,这是一个bug,并且程序没有合理的期望下级程序或其下级程序将正确运行。好的。

argv[0]的可能值包括:好的。

  • /path/to/executable—绝对路径
  • ../bin/executable—相对于PWD
  • bin/executable—相对于PWD
  • ./foo—相对于PWD
  • executablebasename,在路径中查找
  • bin//executable—相对于pwd,非规范
  • src/../bin/executable—相对于pwd,非规范,回溯
  • bin/./echoargc—相对于pwd,非规范

您不应该看到的值:好的。

  • ~/bin/executable—在程序运行之前重写。
  • ~user/bin/executable—在程序运行之前重写
  • alias—在程序运行之前重写
  • $shellvariable—在程序运行之前重写
  • *foo*通配符,在程序运行前重写,不太有用
  • ?foo?通配符,在程序运行前重写,不太有用

此外,这些可能包含非规范路径名和多层符号链接。在某些情况下,同一程序可能有多个硬链接。例如,/bin/ls/bin/ps/bin/chmod/bin/rm等可能是到/bin/busybox的硬链接。好的。

要找到自己,请遵循以下步骤:好的。

  • 将pwd、path和argv[0]保存在程序条目(或库的初始化)上,因为它们稍后可能会更改。好的。

  • 可选:特别是对于非UNIX系统,请分开,但不要丢弃路径名主机/用户/驱动器前缀部分(如果存在);通常位于冒号前面或后面的部分。好的。

  • 如果argv[0]是绝对路径,则将其作为起点。绝对路径可能以"/"开头,但在某些非UNIX系统中,它可能以""开头,或者以驱动器号或名称前缀后跟冒号。好的。

  • 否则,如果argv[0]是一个相对路径(包含"/"或"",但没有以它开头,例如"../../bin/foo",则合并pwd+"/"+argv[0](在程序启动时使用当前工作目录,而不是当前目录)。好的。

  • 否则,如果argv[0]是一个普通的basename(没有斜线),则依次将其与path环境变量中的每个条目组合,并尝试这些项,并使用第一个成功的项。好的。

  • 可选:否则,尝试特定于平台的/proc/self/exe/proc/curproc/file(bsd)、(char *)getauxval(AT_EXECFN)dlgetname(...)(如果存在)。您甚至可以在基于argv[0]的方法之前尝试这些方法,如果它们可用并且您没有遇到权限问题。在不太可能发生的情况下(当您考虑所有系统的所有版本时),它们都存在并且不会失败,它们可能更具权威性。好的。

  • 可选:检查使用命令行参数传入的路径名。好的。

  • 可选:检查环境中由包装脚本显式传入的路径名(如果有)。好的。

  • 可选:作为最后手段,请尝试使用环境变量"uu"。它可能完全指向不同的程序,例如用户shell。好的。

  • 解析符号链接,可能有多个层。存在无限循环的可能性,但是如果它们存在,您的程序可能不会被调用。好的。

  • canonicalize文件名的城市。"resolving Foo类/ /……/酒吧/"/"两栏"。注意这可能存在潜在的变化,如果你在一个网络上的交叉点,立即册封的冰渣,并不总是一个很好的事。在一个网络服务器,"…"符号,可能被用来在两个导线A的路径上的另一个文件服务器的背景下,而不是在客户端。在这个案例中,你可能想要客户上下文SO规范化冰好。也convert样"模式/。/"两个"/"和"/"两个"/"。 壳体内,将多readlink --canonicalizeRESOLVE符号链接和canonicalize name。Chase可能做相似但不安装。如果realpath()canonicalize_file_name(),指尖,可以帮助。

    好。

如果不存在realpath()来编译时,你可能会从一个借一份放纵地分配和许可证的图书馆,它在编译你自己而不是reinventing在轮。修复潜在的缓冲区溢出(通过在输出缓冲区sizeof strncpy(认为)与strcpy())如果你将采用缓冲路径_小于最大。它可能是更容易吗?只是使用一renamed私人复制而不是if exists测试它。从Android的许可腹泻/复制/ BSD:达尔文 http:/ / / /仿生android.googlesource.com平台+ / / / / f077784上游libc库libc FreeBSD / / / / realpath.c stdlib

好。

请注意,多attempts可能是成功或部分成功的,他们可能不是所有的点的两个executable一样,所以你认为verifying executable;然而,你可能不想读的权限,如果你不能读它,我不知道,作为一个治疗失效。在邻近的一个或两个验证你的executable如"/……/ lib目录"你是想找一找。你可以有多个版本,由packaged和局部版和网络版,局域网和局域网和USB驱动的便携式版本,等。和有一个小的可能性,你可能会得到不同的结果从正常的双locating方法。和"_"可以简单的分两个错误的程序。

好。

一个程序可以使用execvedeliberately看到两个argv[0]正常路径与实际使用的程序和corrupt负载路径",_",密码等。虽然没有太多的普遍理由做如此;但这会安全的含义,如果你有vulnerable代码,忽视,这你可以执行环境成龙是山羊的品种包括大学的方式,但不是这一个有限公司,两个(chroot,熔断器的硬链接的文件系统,等),它是可能的两个壳commands上两口,但失败的路径信息。

好。

您不一定需要为非UNIX系统编写代码,但最好了解一些特性,这样您就可以编写代码,这样以后就不会有人很难移植了。请注意,某些系统(dec-vms、dos、urls等)可能具有以冒号结尾的驱动器名或其他前缀,如"c:"、"sys$drive:[foo]bar"和"file:///foo/bar/baz"。旧的DEC VMS系统使用"["和"]"来包含路径的目录部分,但如果您的程序在POSIX环境中编译,则可能会发生更改。某些系统(如vms)可能具有文件版本(结尾用分号分隔)。有些系统使用两个连续斜杠,如"//drive/path/to/file"或"user@host:/path/to/file"(scp命令)或"file://hostname/path/to/file"(url)。在某些情况下(DOS、windoze),路径可能具有不同的分隔符字符-";"vs":"和"vs"/"作为路径分隔符。在csh/tsh中,有"path"(用空格分隔)和"path"(用冒号分隔),但是您的程序应该接收路径,所以您不必担心路径。DOS和其他一些系统可以有以驱动器前缀开头的相对路径。C:foo.exe是指驱动器C上当前目录中的foo.exe,因此您需要查找C:上的当前目录,并将其用于pwd。好的。

我的系统上的符号链接和包装器示例:好的。

1
2
3
4
5
/usr/bin/google-chrome is symlink to
/etc/alternatives/google-chrome  which is symlink to
/usr/bin/google-chrome-stable which is symlink to
/opt/google/chrome/google-chrome which is a bash script which runs
/opt/google/chome/chrome

请注意,用户bill在上面发布了一个链接,指向HP的一个程序,该程序处理argv[0]的三个基本情况。但它需要一些改变:好的。

  • 使用strncat()strncpy(),需要重写所有strcat()strcpy()。即使变量声明为长度pathmax,长度pathmax-1加上连接字符串的长度的输入值为>pathmax,长度pathmax的输入值将不会终止。
  • 它需要重写为一个库函数,而不仅仅是打印出结果。
    • 它无法规范化名称(使用上面链接的realpath代码)
    • 它无法解析符号链接(使用realpath代码)

因此,如果您将HP代码和realpath代码结合在一起并修复它们以抵抗缓冲区溢出,那么您应该有能够正确解释argv[0]的东西。好的。

下面说明了在Ubuntu 12.04上调用同一程序的各种方法的argv[0]的实际值。是的,这个程序被意外地命名为echoargc而不是echoargv。这是使用一个用于干净复制的脚本完成的,但在shell中手动执行会得到相同的结果(除非显式地启用别名,否则别名在脚本中不起作用)。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
cat ~/src/echoargc.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
main(int argc, char **argv)
{
  printf("  argv[0]="%s"
"
, argv[0]);
  sleep(1);  /* in case run from desktop */
}
tcc -o ~/bin/echoargc ~/src/echoargc.c
cd ~
/home/whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
echoargc
  argv[0]="echoargc"
bin/echoargc
  argv[0]="bin/echoargc"
bin//echoargc
  argv[0]="bin//echoargc"
bin/./echoargc
  argv[0]="bin/./echoargc"
src/../bin/echoargc
  argv[0]="src/../bin/echoargc"
cd ~/bin
*echo*
  argv[0]="echoargc"
e?hoargc
  argv[0]="echoargc"
./echoargc
  argv[0]="./echoargc"
cd ~/src
../bin/echoargc
  argv[0]="../bin/echoargc"
cd ~/junk
~/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
~whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
alias echoit=~/bin/echoargc
echoit
  argv[0]="/home/whitis/bin/echoargc"
echoarg=~/bin/echoargc
$echoarg
  argv[0]="/home/whitis/bin/echoargc"
ln -s ~/bin/echoargc junk1
./junk1
  argv[0]="./junk1"
ln -s /home/whitis/bin/echoargc junk2
./junk2
  argv[0]="./junk2"
ln -s junk1 junk3
./junk3
  argv[0]="./junk3"


gnome-desktop-item-edit --create-new ~/Desktop
# interactive, create desktop link, then click on it
  argv[0]="/home/whitis/bin/echoargc"
# interactive, right click on gnome application menu, pick edit menus
# add menu item for echoargc, then run it from gnome menu
 argv[0]="/home/whitis/bin/echoargc"

 cat ./testargcscript 2>&1 | sed -e 's/^/    /g'
#!/bin/bash
# echoargc is in ~/bin/echoargc
# bin is in path
shopt -s expand_aliases
set -v
cat ~/src/echoargc.c
tcc -o ~/bin/echoargc ~/src/echoargc.c
cd ~
/home/whitis/bin/echoargc
echoargc
bin/echoargc
bin//echoargc
bin/./echoargc
src/../bin/echoargc
cd ~/bin
*echo*
e?hoargc
./echoargc
cd ~/src
../bin/echoargc
cd ~/junk
~/bin/echoargc
~whitis/bin/echoargc
alias echoit=~/bin/echoargc
echoit
echoarg=~/bin/echoargc
$echoarg
ln -s ~/bin/echoargc junk1
./junk1
ln -s /home/whitis/bin/echoargc junk2
./junk2
ln -s junk1 junk3
./junk3

这些例子说明,本篇文章中描述的技术应该在各种各样的情况下工作,以及为什么需要某些步骤。好的。

编辑:现在,打印argv[0]的程序已经被更新,以实际找到自己。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2015 by Mark Whitis.  License=MIT style
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include
#include <string.h>
#include <errno.h>

//"look deep into yourself, Clarice"  -- Hanibal Lector
char findyourself_save_pwd[PATH_MAX];
char findyourself_save_argv0[PATH_MAX];
char findyourself_save_path[PATH_MAX];
char findyourself_path_separator='/';
char findyourself_path_separator_as_string[2]="/";
char findyourself_path_list_separator[8]=":";  // could be":;"
char findyourself_debug=0;

int findyourself_initialized=0;

void findyourself_init(char *argv0)
{

  getcwd(findyourself_save_pwd, sizeof(findyourself_save_pwd));

  strncpy(findyourself_save_argv0, argv0, sizeof(findyourself_save_argv0));
  findyourself_save_argv0[sizeof(findyourself_save_argv0)-1]=0;

  strncpy(findyourself_save_path, getenv("PATH"), sizeof(findyourself_save_path));
  findyourself_save_path[sizeof(findyourself_save_path)-1]=0;
  findyourself_initialized=1;
}


int find_yourself(char *result, size_t size_of_result)
{
  char newpath[PATH_MAX+256];
  char newpath2[PATH_MAX+256];

  assert(findyourself_initialized);
  result[0]=0;

  if(findyourself_save_argv0[0]==findyourself_path_separator) {
    if(findyourself_debug) printf("  absolute path
"
);
     realpath(findyourself_save_argv0, newpath);
     if(findyourself_debug) printf("  newpath="%s"
"
, newpath);
     if(!access(newpath, F_OK)) {
        strncpy(result, newpath, size_of_result);
        result[size_of_result-1]=0;
        return(0);
     } else {
    perror("access failed 1");
      }
  } else if( strchr(findyourself_save_argv0, findyourself_path_separator )) {
    if(findyourself_debug) printf("  relative path to pwd
"
);
    strncpy(newpath2, findyourself_save_pwd, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    strncat(newpath2, findyourself_save_argv0, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    realpath(newpath2, newpath);
    if(findyourself_debug) printf("  newpath="%s"
"
, newpath);
    if(!access(newpath, F_OK)) {
        strncpy(result, newpath, size_of_result);
        result[size_of_result-1]=0;
        return(0);
     } else {
    perror("access failed 2");
      }
  } else {
    if(findyourself_debug) printf("  searching $PATH
"
);
    char *saveptr;
    char *pathitem;
    for(pathitem=strtok_r(findyourself_save_path, findyourself_path_list_separator,  &saveptr); pathitem; pathitem=strtok_r(NULL, findyourself_path_list_separator, &saveptr) ) {
       if(findyourself_debug>=2) printf("pathitem="%s"
"
, pathitem);
       strncpy(newpath2, pathitem, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       strncat(newpath2, findyourself_save_argv0, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       realpath(newpath2, newpath);
       if(findyourself_debug) printf("  newpath="%s"
"
, newpath);
      if(!access(newpath, F_OK)) {
          strncpy(result, newpath, size_of_result);
          result[size_of_result-1]=0;
          return(0);
      }
    } // end for
    perror("access failed 3");

  } // end else
  // if we get here, we have tried all three methods on argv[0] and still haven't succeeded.   Include fallback methods here.
  return(1);
}

main(int argc, char **argv)
{
  findyourself_init(argv[0]);

  char newpath[PATH_MAX];
  printf("  argv[0]="%s"
"
, argv[0]);
  realpath(argv[0], newpath);
  if(strcmp(argv[0],newpath)) { printf("  realpath="%s"
"
, newpath); }
  find_yourself(newpath, sizeof(newpath));
  if(1 || strcmp(argv[0],newpath)) { printf("  findyourself="%s"
"
, newpath); }
  sleep(1);  /* in case run from desktop */
}

这里是输出,它证明在之前的每一个测试中,它确实找到了自己。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
tcc -o ~/bin/echoargc ~/src/echoargc.c
cd ~
/home/whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
echoargc
  argv[0]="echoargc"
  realpath="/home/whitis/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin/echoargc
  argv[0]="bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin//echoargc
  argv[0]="bin//echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin/./echoargc
  argv[0]="bin/./echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
src/../bin/echoargc
  argv[0]="src/../bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/bin
*echo*
  argv[0]="echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
e?hoargc
  argv[0]="echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
./echoargc
  argv[0]="./echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/src
../bin/echoargc
  argv[0]="../bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/junk
~/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
~whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
alias echoit=~/bin/echoargc
echoit
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
echoarg=~/bin/echoargc
$echoarg
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
rm junk1 junk2 junk3
ln -s ~/bin/echoargc junk1
./junk1
  argv[0]="./junk1"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
ln -s /home/whitis/bin/echoargc junk2
./junk2
  argv[0]="./junk2"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
ln -s junk1 junk3
./junk3
  argv[0]="./junk3"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"

上面描述的两个GUI启动也可以正确地找到程序。好的。

有一个潜在的陷阱。如果测试前程序是setuid,那么access()函数将删除权限。如果存在这样一种情况,即程序可以被发现是一个提升的用户,而不是一个普通用户,那么可能存在这样一种情况,即这些测试将失败,尽管在这些情况下程序实际上不太可能被执行。可以改用euidaccess()。但是,它可能比实际用户更早地在路径上找到不可访问的程序。好的。好啊。


从GregoryPakosz(它只有一个C文件)中查看Wherami库;它允许您获取到各种平台上当前可执行文件的完整路径。目前,它在GitHub上作为回购提供。


Linux上使用/proc/self/exeargv[0]的另一种选择是使用glibc提供的elf解释器传递的信息:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <sys/auxv.h>

int main(int argc, char **argv)
{
    printf("%s
"
, (char *)getauxval(AT_EXECFN));
    return(0);
}

注意,getauxval是一个glibc扩展,为了保持健壮,您应该检查它,使它不会返回NULL(表示elf解释器没有提供AT_EXECFN参数),但我认为这在Linux上从来就不是问题。


跨平台可靠地工作需要使用ifdef语句。

下面的代码在Windows、Linux、MacOS、Solaris或FreeBSD中查找可执行文件的路径(尽管FreeBSD未经测试)。它使用boost>=1.55.0来简化代码,但如果需要,它很容易删除。正如操作系统和编译器所要求的那样,只需使用像msc-ver和linux这样的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <string>
#include <boost/predef/os.h>

#if (BOOST_OS_WINDOWS)
#  include <stdlib.h>
#elif (BOOST_OS_SOLARIS)
#  include <stdlib.h>
#  include <limits.h>
#elif (BOOST_OS_LINUX)
#  include <unistd.h>
#  include <limits.h>
#elif (BOOST_OS_MACOS)
#  include <mach-o/dyld.h>
#elif (BOOST_OS_BSD_FREE)
#  include <sys/types.h>
#  include <sys/sysctl.h>
#endif

/*
 * Returns the full path to the currently running executable,
 * or an empty string in case of failure.
 */

std::string getExecutablePath() {
#if (BOOST_OS_WINDOWS)
    char *exePath;
    if (_get_pgmptr(&exePath) != 0)
        exePath ="";
#elif (BOOST_OS_SOLARIS)
    char exePath[PATH_MAX];
    if (realpath(getexecname(), exePath) == NULL)
        exePath[0] = '\0';
#elif (BOOST_OS_LINUX)
    char exePath[PATH_MAX];
    ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath));
    if (len == -1 || len == sizeof(exePath))
        len = 0;
    exePath[len] = '\0';
#elif (BOOST_OS_MACOS)
    char exePath[PATH_MAX];
    uint32_t len = sizeof(exePath);
    if (_NSGetExecutablePath(exePath, &len) != 0) {
        exePath[0] = '\0'; // buffer too small (!)
    } else {
        // resolve symlinks, ., .. if possible
        char *canonicalPath = realpath(exePath, NULL);
        if (canonicalPath != NULL) {
            strncpy(exePath,canonicalPath,len);
            free(canonicalPath);
        }
    }
#elif (BOOST_OS_BSD_FREE)
    char exePath[2048];
    int mib[4];  mib[0] = CTL_KERN;  mib[1] = KERN_PROC;  mib[2] = KERN_PROC_PATHNAME;  mib[3] = -1;
    size_t len = sizeof(exePath);
    if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0)
        exePath[0] = '\0';
#endif
    return std::string(exePath);
}

上面的版本返回包括可执行文件名在内的完整路径。如果您希望路径不带可执行文件名,#include boost/filesystem.hpp>并将RETURN语句更改为:

1
return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();


If you ever had to support, say, Mac
OS X, which doesn't have /proc/, what
would you have done? Use #ifdefs to
isolate the platform-specific code
(NSBundle, for example)?

是的,使用#ifdefs隔离特定于平台的代码是实现这一点的常规方法。

另一种方法是使用一个包含函数声明并将实现放在特定于平台的源文件中的无需清理#ifdef的头文件。例如,检查POCO C++库如何为他们的环境类做一些类似的事情。


您可以使用argv[0]并分析path环境变量。看看:一个程序的例子


根据QNX中微子的版本,有不同的方法来查找用于启动运行进程的可执行文件的完整路径和名称。我将进程标识符表示为。尝试以下操作:

  • 如果文件/proc/self/exefile存在,那么其内容就是请求的信息。
  • 如果文件/proc//exefile存在,则其内容是请求的信息。
  • 如果文件/proc/self/as存在,则:
  • 文件。
  • 至少分配一个sizeof(procfs_debuginfo) + _POSIX_PATH_MAX的缓冲区。
  • 把这个缓冲区作为devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...的输入。
  • 将缓冲区强制转换为procfs_debuginfo*
  • 请求的信息位于procfs_debuginfo结构的path字段。警告:由于某些原因,有时qnx会省略文件路径的第一个斜杠/。必要时提前通知/
  • 清理(关闭文件、释放缓冲区等)。
  • 使用文件/proc//as尝试3.中的过程。
  • 尝试dladdr(dlsym(RTLD_DEFAULT,"main"), &dlinfo),其中dlinfoDl_info结构,其dli_fname可能包含请求的信息。
  • 我希望这有帮助。


    获取可执行映像路径名的更可移植方法:

    如果您具有进程ID,ps可以为您提供可执行文件的路径。另外,ps是一个posix实用程序,因此它应该是可移植的

    因此,如果进程ID是249297,那么这个命令只提供路径名。

    1
        ps -p 24297 -o comm --no-heading

    论据的解释

    -P-选择给定的过程

    -o comm-显示命令名(-o cmd选择整个命令行)

    --无标题-不显示标题行,只显示输出。

    C程序可以通过popen运行它。


    阿法克,不是这样的。还有一个问题:如果同一个可执行文件有多个"指向"它的硬链接,您希望得到什么样的答案?(硬链接实际上不是"指向",它们是同一个文件,只是在fs层次结构中的另一个位置。)一旦execve()成功地执行了一个新的二进制文件,它的参数的所有信息都将丢失。


    如果使用c,则可以使用getwd函数:

    1
    2
    3
    4
    5
    6
    int main()
    {      
     char buf[4096];
     getwd(buf);
     printf(buf);
    }

    这将在标准输出上打印可执行文件的当前目录。


    一个程序的绝对值路径在主函数envp的pwd中,还有一个函数在c中称为getenv,所以就是这样。