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

时间:2009-06-21 06:12:48

标签: c++ c linux macos executable

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

如果你曾经不得不支持Mac OS X,它没有/ proc /,你会做什么?使用#ifdefs隔离特定于平台的代码(例如NSBundle)?或者尝试从argv [0],$ PATH和诸如此类推导出可执行文件的路径,冒着在边缘情况下发现错误的风险?

17 个答案:

答案 0 :(得分:332)

某些特定于操作系统的界面:

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

一些shell,包括bash和ksh,set the environment variable "_"到执行程序执行之前的可执行文件的完整路径。在这种情况下,您可以使用getenv("_")来获取它。然而,这是不可靠的,因为并非所有shell都这样做,并且它可以设置为任何内容,或者在执行程序之前不会更改它的父进程遗留下来。

答案 1 :(得分:17)

使用/proc/self/exe是不可移植且不可靠的。在我的Ubuntu 12.04系统上,您必须是root才能阅读/遵循符号链接。这将使Boost示例和可能发布的whereami()解决方案失败。

这篇文章很长,但讨论了实际问题,并提供了实际工作的代码以及针对测试套件的验证。

查找程序的最佳方法是回溯系统使用的相同步骤。这是通过使用argv[0]解析文件系统root,pwd,路径环境并考虑符号链接和路径名规范化来完成的。这是来自记忆,但我在过去成功完成了这项工作并在各种不同的情况下进行了测试。它不能保证工作,但如果它没有,你可能有更大的问题,并且它比任何其他讨论的方法更可靠。在兼容Unix的系统中有一些情况,其中argv[0]的正确处理不会使您进入您的程序,但是您正在一个可认证的破坏环境中执行。从1970年左右开始,它对所有Unix派生系统都相当便携,甚至还有一些非Unix派生系统,因为它基本上依赖于libc()标准功能和标准命令行功能。它应该适用于Linux(所有版本),Android,Chrome OS,Minix,原始贝尔实验室Unix,FreeBSD,NetBSD,OpenBSD,BSD xx,SunOS,Solaris,SYSV,HPUX,Concentrix,SCO,Darwin,AIX,OS X, Nextstep等。稍微修改可能是VMS,VM / CMS,DOS / Windows,ReactOS,OS / 2等。如果程序是直接从GUI环境启动的,它应该将argv[0]设置为绝对路径。

了解几乎每个Unix兼容操作系统上的所有shell都是以相同的方式查找程序,并以几乎相同的方式设置操作环境(使用一些可选的附加功能)。并且任何其他启动程序的程序都应该为该程序创建相同的环境(argv,环境字符串等),就好像它是从shell运行一样,带有一些可选的附加功能。程序或用户可以为其启动的其他从属程序设置偏离此约定的环境,但如果确实如此,则这是一个错误,并且程序没有合理的期望下级程序或其下属将正常运行。

argv[0]的可能值包括:

  • /path/to/executable - 绝对路径
  • ../bin/executable - 相对于pwd
  • bin/executable - 相对于pwd
  • ./foo - 相对于pwd
  • executable - basename,在路径中查找
  • 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](使用当前工作目录从程序启动时,而不是当前)。 / p>

  • 否则,如果argv [0]是普通的基本名称(没有斜杠),那么依次将它与PATH环境变量中的每个条目组合起来并尝试使用第一个成功。

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

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

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

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

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

  • 通过解析" / foo /../ bar /"等子串来规范化文件名;到" / bar /"。请注意,如果您跨越网络安装点,这可能会改变其含义,因此,标准化并不总是一件好事。在网络服务器上," .."在符号链接中可以用于遍历服务器上下文中的另一个文件的路径,而不是在客户端上。在这种情况下,您可能需要客户端上下文,因此规范化是可以的。同样转换" /./"等模式;到" /"和" //"到" /"。 在shell中,readlink --canonicalize将解析多个符号链接并规范化名称。大通可能会做类似但没有安装。 realpath()canonicalize_file_name()(如果有)可能有所帮助。

如果realpath()在编译时不存在,您可以从允许的许可库分发中借用一份副本,并自行编译,而不是重新发明轮子。如果你将使用小于PATH_MAX的缓冲区,修复潜在的缓冲区溢出(传入sizeof输出缓冲区,想想strncpy()vs strcpy())。使用重命名的私有副本可能更容易,而不是测试它是否存在。来自android / darwin / bsd的许可许可证副本: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c

请注意,多次尝试可能会成功或部分成功,并且可能并非都指向相同的可执行文件,因此请考虑验证您的可执行文件;但是,您可能没有阅读权限 - 如果您无法阅读,请不要将其视为失败。或验证您的可执行文件附近的某些内容,例如" ../ lib /"您要查找的目录。您可能有多个版本,打包和本地编译版本,本地和网络版本以及本地和USB驱动器便携版本等,并且您可能会从不同的定位方法获得两个不兼容的结果。并且" _"可能只是指向错误的程序。

使用execve的程序可以故意将argv[0]设置为与用于加载程序和损坏PATH的实际路径不兼容,但是#34; _",pwd等等虽然存在通常没有理由这样做;但如果您的易受攻击的代码忽略了您的执行环境可以通过多种方式进行更改这一事实,包括但不限于此(chroot,fuse文件系统,硬链接等),这可能会产生安全隐患。用于设置PATH的shell命令但无法导出它。

您不一定需要为非Unix系统编写代码,但了解一些特性是个好主意,这样您就可以编写代码,使其不会被编写。对于某人稍后移植很难。请注意,某些系统(DEC VMS,DOS,URL等)可能具有驱动器名称或以冒号结尾的其他前缀,例如" 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),PATH可能有不同的分隔符 - ";" vs":"和" \" vs" /"用于路径分隔符。在csh / tsh中有" path" (用空格分隔)和" PATH"用冒号分隔,但你的程序应该接收PATH,所以你不必担心路径。 DOS和其他一些系统可以具有以驱动器前缀开头的相对路径。 C:foo.exe是指驱动器C上当前目录中的foo.exe,因此您需要在C:上查找当前目录,并将其用于pwd。

我系统上符号链接和包装的一个例子:

/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 posted上方指向HP的程序,该程序处理argv[0]的三个基本情况。但它需要一些改变:

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

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

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

cat ~/src/echoargc.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
main(int argc, char **argv)
{
  printf("  argv[0]=\"%s\"\n", 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]的程序已经更新,实际上找到了自己。

// Copyright 2015 by Mark Whitis.  License=MIT style
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#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\n");
     realpath(findyourself_save_argv0, newpath);
     if(findyourself_debug) printf("  newpath=\"%s\"\n", 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\n");
    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\"\n", 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\n");
    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\"\n", 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\"\n", 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\"\n", argv[0]);
  realpath(argv[0], newpath);
  if(strcmp(argv[0],newpath)) { printf("  realpath=\"%s\"\n", newpath); }
  find_yourself(newpath, sizeof(newpath));
  if(1 || strcmp(argv[0],newpath)) { printf("  findyourself=\"%s\"\n", newpath); }
  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"
  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()代替。但是,它可能会在路径上找到比实际用户更早的无法访问的程序。

答案 2 :(得分:10)

查看Gregory Pakosz的 whereami 库(它只有一个C文件);它允许您在各种平台上获取当前可执行文件的完整路径。目前,它可以作为github上的回购here

答案 3 :(得分:4)

在Linux上使用/proc/self/exeargv[0]的替代方法是使用ELF解释器传递的信息,由glibc提供:

#include <stdio.h>
#include <sys/auxv.h>

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

请注意getauxval是一个glibc扩展,为了强大,你应该检查它不会返回NULL(表明ELF解释器没有提供AT_EXECFN参数),但我不认为这在Linux上实际上是一个问题。

答案 4 :(得分:3)

  

如果你不得不支持,比如Mac   OS X,没有/ proc /,是什么   你会做的吗?使用#ifdefs   隔离特定于平台的代码   (例如,NSBundle)?

是的,使用#ifdefs隔离平台特定代码是传统方式。

另一种方法是拥有一个干净的#ifdef - 更少的包含函数声明的头文件,并将实现放在特定于平台的源文件中。例如,看看Poco C ++库如何为他们的Environment类做类似的事情。

答案 5 :(得分:3)

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

以下代码在Windows,Linux,MacOS,Solaris或FreeBSD中查找可执行文件的路径(尽管FreeBSD未经测试)。它使用boost&gt; = 1.55.0来简化代码,但如果您愿意,可以轻松删除。只需使用像_MSC_VER和__linux这样的定义作为操作系统和编译器所需的。

#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语句更改为:

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

答案 6 :(得分:2)

您可以使用argv [0]并分析PATH环境变量。 请看:A sample of a program that can find itself

答案 7 :(得分:1)

根据 QNX Neutrino 的版本,有多种方法可以找到用于启动正在运行的进程的可执行文件的完整路径和名称。我将进程标识符表示为<PID>。请尝试以下方法:

  1. 如果文件/proc/self/exefile存在,则其内容为请求的信息。
  2. 如果文件/proc/<PID>/exefile存在,则其内容为请求的信息。
  3. 如果文件/proc/self/as存在,则:
    1. open()文件。
    2. 分配至少sizeof(procfs_debuginfo) + _POSIX_PATH_MAX
    3. 的缓冲区
    4. 将该缓冲区作为devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...的输入。
    5. 将缓冲区转换为procfs_debuginfo*
    6. 请求的信息位于path结构的procfs_debuginfo字段。 警告:出于某种原因,有时,QNX会忽略文件路径的第一个斜杠/。在需要时添加/
    7. 清理(关闭文件,释放缓冲区等)。
  4. 使用文件3.
  5. 尝试/proc/<PID>/as中的过程
  6. 尝试dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)其中dlinfoDl_info结构,dli_fname可能包含所请求的信息。
  7. 我希望这会有所帮助。

答案 8 :(得分:1)

当然,这并不适用于所有项目。 尽管如此,QCoreApplication::applicationFilePath()在6年的C ++ / Qt开发中从未让我失败。

当然,在尝试使用文档之前,应该彻底阅读文档:

警告:在Linux上,此函数将尝试从 / proc文件系统。如果失败,则假定argv [0]包含 可执行文件的绝对文件名。该函数还假设 应用程序尚未更改当前目录。

说实话,我认为#ifdef和其他类似的解决方案根本不应该在现代代码中使用。

我确定也存在较小的跨平台库。让他们将所有特定于平台的内容封装在内。

答案 9 :(得分:1)

除了mark4o的答案之外,FreeBSD还具有

const char* getprogname(void)

它也应该在MacOS中可用。可通过libbsd在GNU / Linux中使用。

答案 10 :(得分:1)

如果您正在编写 GPL 代码并使用 GNU 自动工具,那么 gnulib 的 relocatable-prog 模块是处理许多操作系统(包括 Windows 和 macOS)细节的一种可移植方式。

答案 11 :(得分:0)

获取可执行映像的路径名的更便携方式:

如果您有进程ID,那么

ps可以为您提供可执行文件的路径。 ps也是POSIX实用程序,因此它应该是可移植的

因此,如果进程ID为249297,则此命令仅为您提供路径名。

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

参数说明

-p - 选择给定的流程

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

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

C程序可以通过popen运行。

答案 12 :(得分:0)

AFAIK,没有这种方式。而且还有一种模糊性:如果相同的可执行文件有多个硬链接“指向”它,您希望得到什么? (硬链接实际上并不“指向”,它们相同的文件,只是在FS层次结构中的另一个位置。)一旦execve()成功执行了新的二进制文件,所有关于其参数的信息失去了。

答案 13 :(得分:0)

我只有两美分。您可以使用此代码在具有跨平台接口的C / C ++中找到当前应用程序的目录。

void getExecutablePath(char ** path, unsigned int * pathLength)
{
    // Early exit when invalid out-parameters are passed
    if (!checkStringOutParameter(path, pathLength))
    {
        return;
    }

#if defined SYSTEM_LINUX

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    // Return written bytes, indicating if memory was sufficient
    int len = readlink("/proc/self/exe", exePath, PATH_MAX);

    if (len <= 0 || len == PATH_MAX) // memory not sufficient or general error occured
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_WINDOWS

    // Preallocate MAX_PATH (e.g., 4095) characters and hope the executable path isn't longer (including null byte)
    char exePath[MAX_PATH];

    // Return written bytes, indicating if memory was sufficient
    unsigned int len = GetModuleFileNameA(GetModuleHandleA(0x0), exePath, MAX_PATH);
    if (len == 0) // memory not sufficient or general error occured
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_SOLARIS

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    // Convert executable path to canonical path, return null pointer on error
    if (realpath(getexecname(), exePath) == 0x0)
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    unsigned int len = strlen(exePath);
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_DARWIN

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    unsigned int len = (unsigned int)PATH_MAX;

    // Obtain executable path to canonical path, return zero on success
    if (_NSGetExecutablePath(exePath, &len) == 0)
    {
        // Convert executable path to canonical path, return null pointer on error
        char * realPath = realpath(exePath, 0x0);

        if (realPath == 0x0)
        {
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        // Copy contents to caller, create caller ownership
        unsigned int len = strlen(realPath);
        copyToStringOutParameter(realPath, len, path, pathLength);

        free(realPath);
    }
    else // len is initialized with the required number of bytes (including zero byte)
    {
        char * intermediatePath = (char *)malloc(sizeof(char) * len);

        // Convert executable path to canonical path, return null pointer on error
        if (_NSGetExecutablePath(intermediatePath, &len) != 0)
        {
            free(intermediatePath);
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        char * realPath = realpath(intermediatePath, 0x0);

        free(intermediatePath);

        // Check if conversion to canonical path succeeded
        if (realPath == 0x0)
        {
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        // Copy contents to caller, create caller ownership
        unsigned int len = strlen(realPath);
        copyToStringOutParameter(realPath, len, path, pathLength);

        free(realPath);
    }

#elif defined SYSTEM_FREEBSD

    // Preallocate characters and hope the executable path isn't longer (including null byte)
    char exePath[2048];

    unsigned int len = 2048;

    int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };

    // Obtain executable path by syscall
    if (sysctl(mib, 4, exePath, &len, 0x0, 0) != 0)
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#else

    // If no OS could be detected ... degrade gracefully
    invalidateStringOutParameter(path, pathLength);

#endif
}

您可以详细了解here

答案 14 :(得分:0)

但是我想知道是否有一种方便的方法可以通过跨平台接口在C / C ++中找到当前应用程序的目录。

您不能这样做(至少在Linux上如此)

由于可执行文件可以在执行process的过程中运行,因此rename(2)的文件路径将指向另一个目录(同一文件系统)。另请参见syscalls(2)inode(7)

在Linux上,可执行文件甚至可以(原则上)通过调用remove(3)本身unlink(2)。然后Linux kernel应该分配文件,直到没有进程引用它为止。使用proc(5),您可以做一些奇怪的事情(例如/proc/self/exe文件中的rename(2)等)

换句话说,在Linux上,“当前应用程序目录”的概念没有任何意义。

另请参阅Advanced Linux ProgrammingOperating Systems: Three Easy Pieces

还要在OSDEV上查找几种开源操作系统(包括FreeBSDGNU Hurd)。其中有几个提供的接口(API)接近POSIX。

考虑使用(并获得许可)跨平台C ++框架,例如QtPOCO,也许可以通过将它们移植到您喜欢的OS上为它们做出贡献。

答案 15 :(得分:-3)

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

  Intent myIntent = new Intent(activity.this, AlarmReceiver.class);

  PendingIntent pendingIntent = PendingIntent.getBroadcast(Splash.this, 0,   myIntent, 0);

  AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
  Calendar firingCal= Calendar.getInstance();
  Calendar currentCal = Calendar.getInstance();
  firingCal.set(Calendar.HOUR,11); // At the hour you wanna fire
  firingCal.set(Calendar.MINUTE, 0); // Particular minute
  firingCal.set(Calendar.SECOND, 0); // particular second
  long intendedTime = firingCal.getTimeInMillis();
  long currentTime = currentCal.getTimeInMillis();
  if(intendedTime >= currentTime){ 
// you can add buffer time too here to ignore some small differences in        milliseconds
// set from today
 alarmManager.setRepeating(AlarmManager.RTC, intendedTime,        AlarmManager.INTERVAL_DAY, pendingIntent);
  } 
 else{
 // set from next day
 // you might consider using calendar.add() for adding one day to the current day
 firingCal.add(Calendar.DAY_OF_MONTH, 1);
 intendedTime = firingCal.getTimeInMillis();
 alarmManager.setRepeating(AlarmManager.RTC, intendedTime,    AlarmManager.INTERVAL_DAY, pendingIntent);}

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

答案 16 :(得分:-9)

程序的绝对值路径在主函数的envp的PWD中,在C中也有一个名为getenv的函数,所以就是这样。