programing

/proc/self/exe가 없는 현재 실행 파일의 경로 찾기

bestcode 2022. 8. 12. 23:23
반응형

/proc/self/exe가 없는 현재 실행 파일의 경로 찾기

Linux는 /proc/self/exe로 쉽게 할 수 있을 것 같습니다.그러나 크로스 플랫폼 인터페이스를 사용하여 C/C++에서 현재 응용 프로그램의 디렉토리를 찾을 수 있는 편리한 방법이 있는지 알고 싶습니다.몇몇 프로젝트가 argv[0]를 가지고 장난치는 것을 봤지만 완전히 믿을 수 있는 것은 아닌 것 같다.

예를 들어 /proc/가 없는 Mac OS X를 지원해야 한다면 어떻게 하시겠습니까?#ifdefs를 사용하여 플랫폼 고유의 코드(NSBundle 등)를 분리합니다.아니면 엣지 케이스에서 버그를 발견할 위험을 무릅쓰고 argv[0], $PATH 등에서 실행 파일의 경로를 추론해 보시겠습니까?

일부 OS 고유의 인터페이스:

이 정보를 얻기 위해 사용할 수 있는 서드파티 라이브러리도 있습니다.를 들어 프라이드아웃의 답변에 기재되어 있는 whereami, 코멘트에 기재되어 있는Qt, QoreApplication::applicationFilePath()를 사용하고 있는 경우 등이 있습니다.

) 은 ""를 사용하는 입니다.argv[0]할 수 , 를 사용하여 됩니다.$PATH.

bash 및 ksh를 포함한 일부 셸에서는 실행 파일의 _풀 패스로 환경 변수 " " 를 설정합니다.그런 경우에는getenv("_")얻을 수 있습니다.그러나 모든 셸이 이 기능을 수행하는 것은 아니며, 프로그램을 실행하기 전에 변경하지 않은 부모 프로세스에서 이 기능이 설정되거나 남아 있을 수 있기 때문에 신뢰할 수 없습니다.

「 」의 /proc/self/exe을 사용하다Ubuntu 12.04 심볼링크 루트, 의 「Boost」가 .whereami()이치노

이 투고는 매우 길지만 실제 문제에 대해 설명하고 테스트 스위트에 대한 검증과 함께 실제로 도움이 되는 코드를 제시합니다.

사용하고 있는 프로그램을 찾는 가장 좋은 방법은, 시스템이 사용하고 있는 순서와 같은 순서로 되돌리는 것입니다. 하다, 하다, 하다, 하다를 할 수 있어요.argv[0]파일 시스템 루트, pwd, 경로 환경 및 심볼 링크 및 경로 이름 정규화를 고려하여 해결되었습니다.이것은 기억에서 나온 것이지만, 나는 과거에 이것을 성공적으로 했고 다양한 상황에서 테스트했다.동작할 수 있는 것은 아닙니다만, 큰 문제가 없는 경우는, 지금까지 설명한 다른 방법보다 전체적으로 신뢰성이 높아집니다.Unix 에서는 Unix가 올바르게 될 수 .argv[0]프로그램을 시작할 수 없지만 인증할 수 있는 파손된 환경에서 실행할 수 있습니다.또한 1970년경부터 Unix에서 파생된 모든 시스템 및 일부 비 Unix에서 파생된 시스템에도 상당히 이식성이 있습니다. 이는 기본적으로 libc() 표준 기능 및 표준 명령줄 기능에 의존하기 때문입니다.Linux(모든 버전), Android, Chrome OS, Minix, 오리지널 Bell Labs Unix, FreeBSD, NetBSD, OpenBSD, BSD x.x, SunOS, Solaris, SYSV, HP-UX, Concentrix, SCO, Darwin, X에서 작동합니다.VMS, VM/CMS, DOS/Windows, ReactOS, OS/2 을 약간 수정하면 됩니다.프로그램이 GUI 환경에서 직접 기동했을 경우는, 다음과 같이 설정되어 있을 필요가 있습니다.argv[0]절대적인 경로로.

지금까지 출시된 모든 Unix 호환 운영 체제의 거의 모든 셸은 기본적으로 동일한 방식으로 프로그램을 검색하며 운영 환경을 거의 동일하게 설정합니다(옵션 추가 기능 포함).또, 프로그램을 기동하는 그 외의 프로그램에서는, 셸로부터 실행되는 것과 같은 환경(argv, 환경 문자열등)을 작성할 수 있습니다.옵션으로 추가되는 것도 있습니다.프로그램 또는 사용자는 기동하는 다른 하위 프로그램에 대해 이 규칙에서 벗어나는 환경을 설정할 수 있지만, 기동하는 경우 이는 버그이며 프로그램은 하위 프로그램 또는 하위 프로그램이 올바르게 기능할 것이라는 합리적 기대를 갖지 않습니다.

「 」의 한 값.argv[0]다음을 포함합니다.

  • /path/to/executable 경로: " " "
  • ../bin/executable: 에 대한 pwds " " "
  • bin/executable: 에 대한 pwds " " "
  • ./foo: 에 대한 pwds " " "
  • executable path: basename, "basename", "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 시스템의 경우 호스트/사용자/드라이브 접두사 부분을 분리하되 경로 이름인 호스트/드라이브 접두사 부분이 있는 경우 이를 폐기하지 마십시오. 이 부분은 콜론 앞 또는 첫 번째 "/" 뒤에 오는 경우가 많습니다.

  • ifargv[0]절대 경로이므로 시작점으로 사용합니다.절대 경로는 아마 "/"로 시작하지만 일부 Unix 이외의 시스템에서는 "" 또는 드라이브 문자 또는 이름 접두사 뒤에 콜론으로 시작할 수 있습니다.

  • 않은 경우argv[0]는 상대 패스(「/」또는 「」로 시작하지만, 「../../bin/foo」등)로 시작하지 않고, 다음에 pwd+"/"+syslogv[0] 를 조합합니다(프로그램이 개시되었을 때부터 현재의 작업 디렉토리를 사용하고, 현재가 아닙니다).

  • 그렇지 않은 경우 argv[0]가 플레인 기본 이름(슬래시 없음)인 경우 PATH 환경변수의 각 엔트리와 조합하여 이들 엔트리를 시험하고 성공한 첫 번째 엔트리를 사용합니다.

  • 「」: 않으면, 매우 플랫폼 「」주세요./proc/self/exe,/proc/curproc/file및 (BSD), »(char *)getauxval(AT_EXECFN) , , , , 입니다.dlgetname(...)하기 해 볼 도 있다.argv[0]메서드가 하지 않는 based 메서드는 사용 가능합니다.(모든 시스템의 모든 버전을 고려할 때) 이러한 시스템이 존재하며 장애가 발생하지 않는 경우에는 더 신뢰할 수 있는 경우가 있습니다.

  • 옵션: 명령줄 매개 변수를 사용하여 전달된 경로 이름을 확인합니다.

  • 옵션: 래퍼 스크립트에 의해 명시적으로 전달된 환경에서 경로 이름이 있는지 확인합니다(있는 경우).

  • 옵션:마지막 수단으로 환경 변수 "_"를 사용해 보십시오.사용자 셸과 같은 완전히 다른 프로그램을 가리킬 수 있습니다.

  • 심볼 링크를 해결합니다. 여러 계층이 있을 수 있습니다.무한 루프의 가능성이 있지만, 루프가 존재할 경우 프로그램이 호출되지 않을 수 있습니다.

  • 와 같은 foo/../bar/"로 해결하여 .네트워크 마운트 포인트를 넘으면 의미가 바뀔 수 있으므로 캐논라이제이션이 항상 좋은 것은 아닙니다.심볼링크.."를 사용하여 클라이언트 대신 서버 컨텍스트의 다른 파일에 대한 경로를 이동할 수 있습니다.이 경우 클라이언트콘텍스트가 필요하기 때문에 정규화는 문제 없습니다., 「/」를로 합니다.「/」는 「/」를 「/」로 합니다.「」입니다.readlink --canonicalize는 여러 심볼링크를 해결하고 이름을 정규화합니다.Chase도 비슷한 기능을 할 수 있지만 설치되어 있지 않습니다. realpath() ★★★★★★★★★★★★★★★★★」canonicalize_file_name()있으면 도움이 됩니다.

ifrealpath()컴파일 시에는 존재하지 않습니다.허가가 허가된 라이브러리 배포에서 복사본을 빌려 휠을 재작성하지 않고 직접 컴파일할 수 있습니다.PATH_MAX보다 작은 버퍼를 사용하는 경우 잠재적인 버퍼 오버플로우(출력 버퍼 크기 전달, strncpy() vs strcpy())를 수정하십시오.그것이 존재하는지 테스트하는 것보다 이름 변경된 개인 복사본을 사용하는 것이 더 쉬울 수 있습니다.Android/bsd에서 허가된 라이선스 복사: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c

여러 시도가 성공하거나 부분적으로 성공할 수 있으며 모두 동일한 실행 파일을 가리키지는 않을 수 있으므로 실행 파일을 확인하는 것을 고려하십시오. 단, 읽을 수 없는 경우 읽기 권한이 없을 수 있으므로 실패로 간주하지 마십시오.또는 "../lib/" 디렉토리 등 실행 파일에 가까운 것을 확인합니다.여러 버전, 패키지 및 로컬로 컴파일된 버전, 로컬 및 네트워크 버전, 로컬 및 USB 드라이브 휴대용 버전 등이 있을 수 있으며 서로 다른 검색 방법으로 두 가지 호환되지 않는 결과를 얻을 수 있습니다.또한 "_"는 단순히 잘못된 프로그램을 가리킬 수 있습니다.

★★★★★★★★★★★★★★★★를 사용한 프로그램execve로 설정할 수 argv[0]프로그램을 로드하는 데 사용되는 실제 경로와 호환되지 않으며 PATH, "_", pwd 등이 손상되어도 일반적으로 그렇게 할 이유는 없지만, 실행 환경이 이 경로로 제한되지 않고 다양한 방식으로 변경될 수 있다는 사실을 무시하는 취약한 코드가 있으면 보안에 영향을 미칠 수 있습니다(c).links hroot, fuse " " " , " )할 수 .셸 명령어는 PATH를 설정할 수 있지만 내보낼 수 없습니다.

반드시 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 및 윈도우즈) PATH에 ";" 대 ":" 및 경로 구분자로 "/"와 같은 구분 문자가 다를 수 있습니다.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

사용자가 위의 링크를 HP의 프로그램에 게시한 것에 주의해 주십시오.이 프로그램에서는, 다음의 3개의 기본적인 케이스가 취급되고 있습니다.argv[0]몇 변경이 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.

  • 을 다시 .strcat() ★★★★★★★★★★★★★★★★★」strcpy()strncat() ★★★★★★★★★★★★★★★★★」strncpy()변수가 길이 PATHMAX로 선언되어 있어도 길이 PATHMAX-1 입력값과 연결된 문자열의 길이는 PATHMAX >이며 길이 PATHMAX 입력값은 종료되지 않습니다.
  • 결과만 출력하는 것이 아니라 라이브러리 기능으로 다시 작성해야 합니다.
  • 이름을 정규화할 수 없습니다(위에서 링크한 실제 경로 코드 사용).
  • 심볼릭 링크를 해결할 수 없습니다(실제 경로 코드 사용).

오버플로에 의 realpath를 해석할 수 합니다.argv[0].

은 실제 이다.argv[0]Ubuntu 12.04에서 동일한 프로그램을 실행하는 다양한 방법을 제공합니다.이 은 실수로 라는 이름이 .이것은 클린 복사에 스크립트를 사용했지만 셸에서 수동으로 실행하면 동일한 결과를 얻을 수 있습니다(에일리어스를 명시적으로 활성화하지 않는 한 스크립트에서 작동하지 않습니다).

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"

위에서 설명한 2개의 GUI를 기동해도 프로그램은 올바르게 검출됩니다.

잠재적인 함정이 하나 있다.access()함수는 테스트 전에 프로그램이 setuid인 경우 권한을 삭제합니다.프로그램이 일반 사용자로 인식되지 않고 상승된 사용자로 인식될 수 있는 경우 이러한 상황에서 실제로 프로그램이 실행될 가능성은 낮지만 이러한 테스트에 실패할 수 있습니다.euidaccess()는 euidaccess()로 지정합니다.그러나 실제 사용자가 찾을 수 있는 것보다 경로에서 액세스 불가능한 프로그램을 더 빨리 찾을 수 있습니다.

Gregory Pakosz의 whereami 라이브러리mark4o의 투고에서 언급된 API를 사용하여 다양한 플랫폼에 이를 구현합니다.이는 다양한 플랫폼의 특성에 관심이 없고 휴대용 프로젝트에 적합한 솔루션이 필요한 경우 가장 흥미롭습니다.

기술 시 지원되는 플랫폼은 다음과 같습니다.

  • 창문들
  • 리눅스
  • iOS
  • 안드로이드
  • 중성미자
  • FreeBSD
  • 넷BSD
  • DragonFly BSD
  • SunOS

라이브러리는 다음과 같이 구성되어 있습니다.whereami.c ★★★★★★★★★★★★★★★★★」whereami.h는, 및 「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;
}

에서 Linux를 /proc/self/exe ★★★★★★★★★★★★★★★★★」argv[0]는 ELF, 다음과 같이에 의해 합니다.「 」 、 「 ELF 」 、 「 Glibc 」 、 「 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가.NULLELF (ELF 인터프리터가 ).AT_EXECFNparameter는 Linuxparameter)에서는 실제로

플랫폼 전체에서 이 작업을 안정적으로 수행하려면 #ifdef 문을 사용해야 합니다.

다음 코드는 Windows, Linux, MacOS, Solaris 또는 FreeBSD에서 실행 파일의 경로를 찾습니다(FreeB이지만).SD는 테스트되지 않았습니다).Boost 1.55.0(또는 그 이후)을 사용하여 코드를 심플하게 만들지만 원하는 경우 쉽게 제거할 수 있습니다.OS 및 컴파일러가 필요로 하는 _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 strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();

예를 들어 /proc/가 없는 Mac OS X를 지원해야 한다면 어떻게 하시겠습니까?#ifdefs를 사용하여 플랫폼 고유의 코드(NSBundle 등)를 분리합니다.

플랫폼 를 「」, 「」로 분리합니다.#ifdefs전통적인 방식입니다.

다른 은 을 입니다.#ifdef-less header는 함수 선언을 포함하고 구현을 플랫폼 고유의 소스 파일에 저장합니다.

예를 들어 POCO(Portable Components) C++ 라이브러리가 환경 클래스에 대해 어떻게 유사한 작업을 수행하는지 확인하십시오.

mark4o의 답변 외에 FreeBSD는

const char* getprogname(void)

MacOS에서도 사용할 수 있습니다.Libbsd를 통해 GNU/Linux에서 사용할 수 있습니다.

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. 를 「」의 으로서 「」에 건네 주세요.devctl(fd, DCMD_PROC_MAPDEBUG_BASE,....
    4. 를 a에 던지다.procfs_debuginfo*.
    5. 는 <고객명>에 있습니다.pathprocfs_debuginfo구조.경고:어떤 이유로, 가끔 QNX 첫번째 슬래시 생략합니다./파일 경로 중에서. 를 추가하세요./필요할 때.
    6. (파일을 닫고 자유롭게 버퍼 등) 치워라.
  4. 이 를 시험해 보세요.3. 「」을 해 주세요./proc/<PID>/as.
  5. ★★를 해 보세요.dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)서 ''는dlinfo는 입니다.Dl_info structure whose의 dli_fname에는 요청된 정보가 포함되어 있을 수 있습니다.

이게 도움이 됐으면 좋겠어요.

AFAIK, 그런 식으로는 안돼.또, 애매한 점도 있습니다.같은 실행 파일에 복수의 하드 링크가 「포인팅」되어 있는 경우, 그 해답으로서 무엇을 얻을 수 있을까요(하드 링크는 실제로는 「포인팅」되지 않고, 파일 시스템 계층내의 다른 장소에 있는 파일입니다).

(Once)execve()새로운 바이너리가 정상적으로 실행되어 원래 프로그램에 대한 인수에 대한 모든 정보가 손실됩니다.

GPLed 코드를 작성하고 GNU autotools를 사용하고 있다면 많은 OS(Windows 및 MacOS 포함)에서 세부사항을 관리하는 휴대용 방법은 gnulib 모듈입니다.

물론 모든 프로젝트에 적용되는 것은 아닙니다.★★★★★★★★★★★★★★★.QCoreApplication::applicationFilePath()C++/QT 6은 C++/QT입니다.

물론 설명서를 사용하기 전에 자세히 읽어보아야 합니다.

경고: Linux에서는 이 함수가 /proc 파일시스템에서 경로를 가져옵니다.실패할 경우 argv[0]에 실행 파일의 절대 파일 이름이 포함되어 있다고 가정합니다.또, 이 함수는, 현재의 디렉토리가 애플리케이션에 의해서 변경되지 않은 것을 전제로 하고 있습니다.

말하면, 저는 '아까운 친구'라고 합니다.#ifdef그리고 그와 같은 다른 솔루션들은 현대 코드에는 전혀 사용되지 않아야 합니다.

더 작은 크로스 플랫폼 라이브러리도 존재한다고 확신합니다.플랫폼 고유의 모든 것을 내부에 캡슐화합니다.

그러나 크로스 플랫폼 인터페이스를 사용하여 C/C++에서 현재 응용 프로그램의 디렉토리를 찾을 수 있는 편리한 방법이 있는지 알고 싶습니다.

이 작업은 할 수 없습니다(최소한 Linux에서는).

실행 파일은 실행 중인 프로세스를 실행하는 동안 파일 경로의 이름을 (같은 파일 시스템의) 다른 디렉터리로 바꿀 수 있습니다.syscalls (2) inode(7)도 참조해 주세요.

Linux 에서는 실행 파일이 unlink(2)를 호출함으로써 (원칙적으로) 삭제(3)할 수도 있습니다.그러면 Linux 커널은 프로세스가 더 이상 참조하지 않을 때까지 파일을 할당된 상태로 유지해야 합니다.proc(5)사용하면 다음과 같은 이상한 일을 할 수 있습니다(예: rename(2))./proc/self/exe ) ( )

즉, Linux 에서는, 「현재 애플리케이션의 디렉토리」라는 개념은 의미가 없습니다.

Linux의 고급 프로그래밍 및 운영 체제도 확인. 3가지 간단한 조각으로 더 많은 것.

OSDEV에서 몇 가지 오픈 소스 운영체제(FreeB 포함)를 찾아보세요.SD 또는 GNU Hurd).이들 중 몇 개는 POSIX에 가까운 인터페이스(API)를 제공합니다.

QtPOCO와 같은 크로스 플랫폼 C++ 프레임워크를 (허가를 받아) 사용하는 것을 검토해 주십시오.또, 마음에 드는 OS에 이식하는 것으로, 이러한 프레임워크에 공헌할 수도 있습니다.

argv[0]를 사용하여 PATH 환경변수를 분석할 수 있습니다.참조: 자신을 찾을있는 프로그램의 샘플

내 의견일 뿐이야.이 코드를 사용하면 크로스 플랫폼인터페이스가 있는 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
}

여기서 자세히 보실 수 있습니다.

언급URL : https://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe

반응형