C++ 查找当前可执行文件';s路径,不带/proc/self/exe

C++ 查找当前可执行文件';s路径,不带/proc/self/exe,c++,c,linux,macos,executable,C++,C,Linux,Macos,Executable,在我看来,Linux很容易使用/proc/self/exe。但是我想知道是否有一种方便的方法可以在具有跨平台接口的C/C++中找到当前应用程序的目录。我见过一些项目使用argv[0]胡闹,但它似乎并不完全可靠 如果你曾经支持,比如说,MacOSX,它没有/proc/,你会怎么做?使用#ifdefs隔离特定于平台的代码(例如,NSBundle)?或者尝试从argv[0]、$path等等推断可执行文件的路径,冒着在边缘情况下发现bug的风险?AFAIK,没有这种方法。还有一个模棱两可的问题:如果同一

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


如果你曾经支持,比如说,MacOSX,它没有/proc/,你会怎么做?使用#ifdefs隔离特定于平台的代码(例如,NSBundle)?或者尝试从argv[0]、$path等等推断可执行文件的路径,冒着在边缘情况下发现bug的风险?

AFAIK,没有这种方法。还有一个模棱两可的问题:如果同一个可执行文件有多个指向它的硬链接,您希望得到什么样的答案?(硬链接实际上并不“指向”,它们是同一个文件,只是位于文件系统层次结构中的另一个位置。)


一旦
execve()
成功执行新的二进制文件,有关原始程序参数的所有信息都将丢失。

您可以使用argv[0]并分析PATH环境变量。 看看:

如果你曾经支持过,比如说,麦克 OSX没有/proc/,什么 你会怎么做?使用#ifdef 隔离特定于平台的代码 (例如,NSBundle)

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

另一种方法是使用干净的
#ifdef
-less头,其中包含函数声明,并将实现放在特定于平台的源文件中


例如,检查POCO(便携式组件)C++库如何为他们的类做类似的事情。

< P>一些OS特定的接口:

  • Mac OS X:()
  • Linux:
  • Solaris:
  • FreeBSD:
  • FreeBSD如果有procfs:
    readlink/proc/curproc/file
    (FreeBSD默认没有procfs)
  • NetBSD:
    readlink/proc/curproc/exe
  • 蜻蜓BSD:
    readlink/proc/curproc/file
  • Windows:使用
    hModule
    =
    NULL
还有一些第三方库可以用来获取这些信息,例如,或者如果您正在使用Qt,如注释中所述

可移植(但不太可靠)的方法是使用
argv[0]
。尽管调用程序可以将其设置为任何内容,但按照约定,它可以设置为可执行文件的路径名,也可以设置为使用
$path
找到的名称


一些shell(包括bash和ksh)在执行可执行文件之前会将其复制到可执行文件的完整路径。在这种情况下,您可以使用
getenv(“”)
来获取它。但是,这是不可靠的,因为并非所有shell都这样做,它可以被设置为任何值,或者是在执行程序之前没有更改它的父进程遗留下来的。

在Linux上使用
/proc/self/exe
argv[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);
}
#包括
#包括
int main(int argc,字符**argv)
{
printf(“%s\n”,(char*)getauxval(AT_EXECFN));
返回(0);
}
请注意,
getauxval
是一个glibc扩展,为了保持健壮性,您应该检查它,使其不会返回
NULL
(表示ELF解释器没有提供
AT_EXECFN
参数),但我认为这在Linux上从来都不是问题。

该解释器为各种平台实现了这一点,使用中提到的API。如果您“只是”需要一个可移植项目的解决方案,并且对各种平台的特性不感兴趣,那么这是最有趣的

在撰写本文时,受支持的平台包括:

  • 窗户
  • Linux
  • 苹果
  • iOS
  • 安卓
  • QNX中微子
  • FreeBSD
  • NetBSD
  • 蜻蜓BSD
  • 苏诺斯
该库由
wheremi.c
wheremi.h
组成,并根据MIT和WTFPL2获得许可。将文件放入项目中,包括标题并使用它:

#include "whereami.h"

int main() {
  int length = wai_getExecutablePath(NULL, 0, NULL);
  char* path = (char*)malloc(length + 1);
  wai_getExecutablePath(path, length, &dirname_length);
  path[length] = '\0';

  printf("My path: %s", path);

  free(path);
  return 0;
}

要跨平台可靠地执行此操作,需要使用#ifdef语句

下面的代码在Windows、Linux、MacOS、Solaris或FreeBSD中查找可执行文件的路径(尽管FreeBSD未经测试)。它使用 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);
}

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

  • 如果文件
    /proc/self/exefile
    存在,则其内容为请求的信息
  • 如果文件
    /proc//exefile
    存在,则其内容是请求的信息
  • 如果文件
    /proc/self/as
    存在,则:
  • open()
    打开文件
  • 分配一个缓冲区,至少,
    sizeof(procfs\u debuginfo)+\u POSIX\u PATH\u MAX
  • 将该缓冲区作为输入提供给devctl(fd、DCMD\u PROC\u MAPDEBUG\u BASE,)
  • 将缓冲区强制转换为
    procfs\u debuginfo*
  • 请求的信息位于
    procfs\u debuginfo
    结构的
    path
    字段中。警告:由于某些原因,QNX有时会忽略文件路径的第一个斜杠
    /
    。需要时,请在前面加上
    //code>
  • 清理(关闭文件、释放缓冲区等)
  • 使用文件
    /proc//as
    尝试
    3.
    中的过程
  • 尝试
    dladdr(dlsym(RTLD\u默认值,“main”),&dlinfo)
    其中
    dlinfo
    是一个
    Dl\u info
    结构,其
    dli\u fname
    可能包含请求的
    return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();
    
    /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
    
    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
    
    // 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"
    
    const char* getprogname(void)
    
    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
    }