/ proc / self / exe없이 현재 실행 파일 경로 찾기
Linux는 / proc / self / exe로 쉽게 사용할 수 있습니다. 그러나 크로스 플랫폼 인터페이스를 사용하여 C / C ++에서 현재 응용 프로그램 디렉토리를 찾는 편리한 방법이 있는지 알고 싶습니다. argv [0]와 관련이있는 일부 프로젝트를 보았지만 완전히 신뢰할 수있는 것 같지는 않습니다.
예를 들어, / proc /가없는 Mac OS X을 지원해야한다면 어떻게해야합니까? #ifdefs를 사용하여 플랫폼 별 코드 (예 : NSBundle)를 분리 하시겠습니까? 아니면 argv [0], $ PATH 등에서 실행 파일의 경로를 추론하여 가장자리에 버그가있을 위험이 있습니까?
일부 OS 관련 인터페이스 :
- 맥 OS X :
_NSGetExecutablePath()
( man 3 dyld ) - 리눅스 :
readlink /proc/self/exe
- 솔라리스 :
getexecname()
- FreeBSD :
sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
- Procfs가있는 경우 FreeBSD :
readlink /proc/curproc/file
(FreeBSD에는 기본적으로 procfs가 없습니다) - NetBSD :
readlink /proc/curproc/exe
- DragonFly BSD :
readlink /proc/curproc/file
- Windows :
GetModuleFileName()
withhModule
=NULL
이식 가능하지만 신뢰성이 떨어지는 방법은을 사용하는 것 argv[0]
입니다. 호출 프로그램에 의해 무엇이든 설정할 수 있지만, 관례 적으로 실행 파일의 경로 이름 또는을 사용하여 찾은 이름으로 설정됩니다 $PATH
.
bash 및 ksh를 포함한 일부 쉘 은 실행하기 전에 환경 변수 " _
" 를 실행 파일의 전체 경로로 설정합니다. 이 경우 사용할 getenv("_")
수 있습니다. 그러나 이것은 모든 쉘이이를 수행하지는 않기 때문에 신뢰할 수 없으며 프로그램으로 실행하기 전에 변경하지 않은 상위 프로세스에서 설정되거나 남아있을 수 있습니다.
의 사용은 /proc/self/exe
비 휴대용 및 신뢰할 수 없다. Ubuntu 12.04 시스템에서 심볼릭 링크를 읽고 따르려면 루트 사용자 여야합니다. 이렇게하면 Boost 예제가 작성되고 whereami()
게시 된 솔루션이 실패 할 수 있습니다.
이 게시물은 매우 길지만 실제 문제에 대해 설명하고 실제로 테스트 스위트에 대한 유효성 검사와 함께 작동하는 코드를 제공합니다.
프로그램을 찾는 가장 좋은 방법은 시스템이 사용하는 것과 동일한 단계를 다시 추적하는 것입니다. 이는 argv[0]
파일 시스템 루트, pwd, 경로 환경에 대해 해결 을 사용 하고 심볼릭 링크 및 경로 이름 정규화를 고려 하여 수행됩니다 . 이것은 메모리에서 왔지만 과거에는이 작업을 성공적으로 수행했으며 다양한 상황에서 테스트했습니다. 작동한다고 보장되지는 않지만 문제가 더 크지 않으면 논의 된 다른 방법보다 전체적으로 더 안정적입니다. Unix 호환 시스템에는 적절한 처리 방법이 있습니다argv[0]
당신을 당신의 프로그램으로 데려 가지 않지만 당신은 인증 된 환경에서 실행하고 있습니다. 1970 년 이래로 모든 유닉스 파생 시스템과 상당히 유닉스 파생 시스템에도 이식성이 뛰어나다. 기본적으로 libc () 표준 기능과 표준 명령 줄 기능에 의존하기 때문이다. Linux (모든 버전), Android, Chrome OS, Minix, 원본 Bell Labs Unix, FreeBSD, NetBSD, OpenBSD, BSD xx, SunOS, Solaris, SYSV, HPUX, Concentrix, SCO, Darwin, AIX, OS X, 다음 단계 등 약간만 수정하면 VMS, VM / CMS, DOS / Windows, ReactOS, OS / 2 등이 될 수 있습니다. 프로그램이 GUI 환경에서 직접 시작된 argv[0]
경우 절대 경로로 설정해야 합니다.
기본적으로 출시 된 모든 Unix 호환 운영 체제의 거의 모든 셸은 기본적으로 동일한 방식으로 프로그램을 찾고 운영 환경을 거의 같은 방식으로 설정합니다 (일부 옵션 추가 옵션 포함). 그리고 프로그램을 시작하는 다른 프로그램은 쉘에서 실행되는 것과 같은 환경 (argv, 환경 문자열 등)을 추가 옵션과 함께 생성 할 것으로 예상됩니다. 프로그램이나 사용자는 실행되는 다른 하위 프로그램에 대해이 규칙에서 벗어난 환경을 설정할 수 있지만, 그렇지 않은 경우 이는 버그이며 하위 프로그램이나 해당 하위 프로그램이 올바르게 작동 할 것으로 기대할 수 없습니다.
가능한 값은 argv[0]
다음 과 같습니다.
/path/to/executable
— 절대 경로../bin/executable
— pwd와 관련bin/executable
— pwd와 관련./foo
— pwd와 관련executable
— 기본 이름, 경로에서 찾기bin//executable
— 비정규적인 암호src/../bin/executable
— 비밀번호, 비정규, 역 추적bin/./echoargc
— 비정규적인 암호
볼 수없는 값 :
~/bin/executable
— 프로그램이 실행되기 전에 다시 작성되었습니다.~user/bin/executable
— 프로그램이 실행되기 전에 다시 작성alias
— 프로그램이 실행되기 전에 다시 작성$shellvariable
— 프로그램이 실행되기 전에 다시 작성*foo*
— 와일드 카드, 프로그램이 실행되기 전에 다시 작성되었지만 그다지 유용하지 않음?foo?
— 와일드 카드, 프로그램이 실행되기 전에 다시 작성되었지만 그다지 유용하지 않음
또한 여기에는 비정규 경로 이름과 여러 계층의 심볼릭 링크가 포함될 수 있습니다. 경우에 따라 동일한 프로그램에 대한 여러 개의 하드 링크가있을 수 있습니다. 예를 들어, /bin/ls
, /bin/ps
, /bin/chmod
, /bin/rm
, 등으로 하드 링크를 할 수있다 /bin/busybox
.
자신을 찾으려면 아래 단계를 따르십시오.
나중에 변경 될 수 있으므로 프로그램 (또는 라이브러리 초기화)에 pwd, PATH 및 argv [0]을 저장하십시오.
선택 사항 : 특히 비 유닉스 시스템의 경우 경로 이름 호스트 / 사용자 / 드라이브 접두어 부분이 있으면 분리하지만 버리지 마십시오. 콜론 앞에 오는 부분이나 초기 "//"뒤에 오는 부분.
경우
argv[0]
절대 경로이며, 출발 지점 것을 사용한다. 절대 경로는 아마도 "/"로 시작하지만 유닉스가 아닌 일부 시스템에서는 "\"로 시작하거나 드라이브 문자 나 이름 접두사 뒤에 콜론이 올 수 있습니다.그렇지 않으면
argv[0]
상대 경로 인 경우 ( "/"또는 "\"를 포함하지만 "../../bin/foo"와 같이 시작하지 않는 경우 pwd + "/"+ argv [0]을 사용하십시오 (사용 현재가 아닌 프로그램 시작 시점의 현재 작업 디렉토리).그렇지 않으면 argv [0]이 일반 기본 이름 (슬래시 없음) 인 경우 PATH 환경 변수의 각 항목과 차례로 조합하여 시도하고 성공한 첫 번째 항목을 사용하십시오.
옵션 : 그렇지 않으면 아주 플랫폼 특정 시도
/proc/self/exe
,/proc/curproc/file
(BSD)를하고(char *)getauxval(AT_EXECFN)
,하고dlgetname(...)
있는 경우.argv[0]
사용 가능하고 권한 문제가 발생하지 않는 경우 이전 기반 방법을 사용해 볼 수도 있습니다. 거의 존재하지 않는 경우 (모든 시스템의 모든 버전을 고려할 때) 존재하고 실패하지 않는 경우에는 더 신뢰할 수 있습니다.선택 사항 : 명령 행 매개 변수를 사용하여 전달 된 경로 이름을 확인하십시오.
선택 사항 : 랩퍼 스크립트가 명시 적으로 전달한 환경의 경로 이름이 있는지 확인하십시오 (있는 경우).
선택 사항 : 최후의 수단으로 환경 변수 "_"를 시도하십시오. 사용자 쉘과 같은 다른 프로그램을 가리킬 수도 있습니다.
심볼릭 링크를 해결하면 여러 계층이있을 수 있습니다. 무한 루프의 가능성이 있지만, 존재한다면 프로그램이 호출되지 않을 것입니다.
"/foo/../bar/"와 같은 하위 문자열을 "/ bar /"로 해석하여 파일 이름을 정규화하십시오. 네트워크 탑재 지점을 통과하면 의미가 변경 될 수 있으므로 정식화가 항상 좋은 것은 아닙니다. 네트워크 서버에서 symlink의 ".."를 사용하여 클라이언트 대신 서버 컨텍스트에서 다른 파일의 경로를 탐색 할 수 있습니다. 이 경우 클라이언트 컨텍스트를 원할 수 있으므로 정규화가 가능합니다. 또한 "/./"와 같은 패턴을 "/"로, "//"를 "/"로 변환하십시오. 쉘에서
readlink --canonicalize
여러 심볼릭 링크를 해결하고 이름을 정식화합니다. 체이스는 비슷하지만 설치되지는 않습니다.realpath()
또는있는canonicalize_file_name()
경우 도움이 될 수 있습니다.
경우 realpath()
컴파일시에 존재하지 않는, 당신은 permissively 라이센스 라이브러리 분포 사본을 빌려와 자신을 대신 바퀴를 재발 명보다 컴파일 수 있습니다. PATH_MAX보다 작은 버퍼를 사용하려면 잠재적 버퍼 오버 플로우 (출력 버퍼의 크기를 전달하고 strncpy ()와 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, "_", pwd 등을 손상시킬 수는 있지만 일반적으로 그렇게 할 이유는 많지 않습니다. 그러나 실행 환경이이를 포함하여 다양한 방식으로 변경 될 수 있다는 사실을 무시하는 취약한 코드가있는 경우 보안에 영향을 줄 수 있습니다 (chroot, 퓨즈 파일 시스템, 하드 링크 등). 쉘 명령이 PATH를 설정하지만 내 보내지 못합니다.
유닉스 이외의 시스템에서는 반드시 코딩 할 필요는 없지만 일부 특성을 알고 있으면 나중에 다른 사람이 이식하기 어려운 방식으로 코드를 작성할 수 있습니다. . 일부 시스템 (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과 같이 두 개의 연속 슬래시를 사용합니다. (공백으로 구분) 및 "PATH"는 콜론으로 구분되지만 프로그램은 PATH를 수신해야하므로 경로에 대해 걱정할 필요가 없습니다. DOS 및 일부 다른 시스템에는 드라이브 접두어로 시작하는 상대 경로가있을 수 있습니다. C : foo.exe는 C 드라이브의 현재 디렉토리에서 foo.exe를 참조하므로 C :에서 현재 디렉토리를 찾아 pwd에 사용해야합니다. (공백으로 구분) 및 "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 프로그램에 대한 링크를 게시 했습니다 argv[0]
. 그래도 약간의 변경이 필요합니다.
- 모두 다시 작성해야 할 것입니다
strcat()
및strcpy()
사용에strncat()
와strncpy()
. 변수가 길이 PATHMAX로 선언되었지만 길이 PATHMAX-1의 입력 값과 연결된 문자열의 길이는> PATHMAX이고 길이 PATHMAX의 입력 값은 종료되지 않습니다. - 결과를 인쇄하기보다는 라이브러리 함수로 다시 작성해야합니다.
- 이름을 정식화하지 못합니다 (위의 링크 코드를 사용하십시오)
- 심볼릭 링크를 확인하지 못합니다 (실시간 코드 사용).
따라서 HP 코드와 realpath 코드를 모두 결합하고 버퍼 오버플로에 견딜 수 있도록 수정하면 올바르게 해석 할 수있는 것이 있어야합니다 argv[0]
.
다음 argv[0]
은 Ubuntu 12.04에서 동일한 프로그램을 호출하는 다양한 방법 에 대한 실제 값을 보여줍니다 . 그리고 네, 프로그램 이름은 실수로 echoargv 대신 echoargc로 명명되었습니다. 이것은 깨끗한 복사를 위해 스크립트를 사용하여 수행되었지만 쉘에서 수동으로 수행하면 동일한 결과를 얻습니다 (명명하게 사용하도록 설정하지 않으면 스크립트에서 별칭이 작동하지 않음).
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 실행도 프로그램을 올바르게 찾습니다.
하나의 잠재적 함정이 있습니다. 이 access()
기능은 테스트 전에 프로그램이 setuid 인 경우 권한을 삭제합니다. 일반 사용자가 아닌 관리자 권한으로 프로그램을 찾을 수있는 상황이있는 경우에는 이러한 테스트가 실패 할 수 있지만 이러한 상황에서 프로그램을 실제로 실행할 수는 없습니다. 대신 euidaccess ()를 사용할 수 있습니다. 그러나 실제 사용자보다 경로에서 더 일찍 액세스 할 수없는 프로그램을 찾을 수 있습니다.
Gregory Pakosz (단일 C 파일이 있음) 의 whereami 라이브러리를 확인하십시오 . 다양한 플랫폼에서 현재 실행 파일의 전체 경로를 얻을 수 있습니다. 현재 github에서 리포지토리로 사용할 수 있습니다 .
Linux에서 /proc/self/exe
또는 argv[0]
glibc에서 제공하는 ELF 인터프리터가 전달한 정보 를 사용하거나 사용하는 다른 방법 은 다음과 같습니다.
#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
매개 변수),하지만이 이제까지 실제로 리눅스에 문제가 있다고 생각하지 않습니다.
예를 들어, / proc /가없는 Mac OS X을 지원해야한다면 어떻게해야합니까? #ifdefs를 사용하여 플랫폼 별 코드 (예 : NSBundle)를 분리 하시겠습니까?
예, 플랫폼 별 코드를 격리하는 #ifdefs
것이 일반적인 방법입니다.
또 다른 접근법은 깔끔한 #ifdef
헤더 를 가지고 함수 선언을 포함하고 구현을 플랫폼 별 소스 파일에 넣는 것입니다. 예를 들어, Poco C ++ 라이브러리가 환경 클래스 와 유사한 기능을 수행하는 방법을 확인하십시오 .
여러 플랫폼에서이 작업을 안정적으로 수행하려면 #ifdef 문을 사용해야합니다.
아래 코드는 Windows, Linux, MacOS, Solaris 또는 FreeBSD에서 실행 파일의 경로를 찾습니다 (FreeBSD는 테스트되지 않았 음). 그것은 사용 부스트 코드를 단순화하기> = 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 문을 다음과 같이 변경하십시오.
return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();
argv [0]을 사용하고 PATH 환경 변수를 분석 할 수 있습니다. 살펴보기 : 스스로 찾을 수있는 프로그램 샘플
AFAIK, 그런 식으로하지 않습니다. 또한 애매 모호함이 있습니다. 동일한 실행 파일에 여러 개의 "하드"링크가있는 경우 응답으로 무엇을 얻고 싶습니까? (하드 링크가 실제로 "지점"안 된다 단지 FS 계층 구조의 다른 장소에서, 같은 파일.)에서 execve되면 () 성공적으로 새로운 바이너리를 실행 인수에 대한 모든 정보가 손실됩니다.
QNX Neutrino 버전에 따라 실행중인 프로세스를 시작하는 데 사용 된 실행 파일의 전체 경로와 이름을 찾는 방법은 여러 가지가 있습니다. 프로세스 식별자를로 표시합니다 <PID>
. 다음을 시도하십시오 :
- 파일
/proc/self/exefile
이 존재하면 해당 내용이 요청 된 정보입니다. - 파일
/proc/<PID>/exefile
이 존재하면 해당 내용이 요청 된 정보입니다. - 파일
/proc/self/as
이 존재하면 다음을 수행하십시오.open()
파일.- 적어도 버퍼를 할당하십시오
sizeof(procfs_debuginfo) + _POSIX_PATH_MAX
. - 해당 버퍼를에 입력으로 제공하십시오
devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
. - 버퍼를로 캐스팅하십시오
procfs_debuginfo*
. - 요청 된 정보는 구조 의
path
필드에procfs_debuginfo
있습니다. 경고 : 어떤 이유로 QNX/
가 파일 경로 의 첫 번째 슬래시 를 생략하는 경우가 있습니다./
필요할 때 추가하십시오 . - 정리하십시오 (파일을 닫고 버퍼를 비우는 등).
3.
파일과 함께 절차를 시도 하십시오/proc/<PID>/as
.- 시도
dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)
여기서dlinfo
A는Dl_info
그 구조dli_fname
요청 된 정보를 포함 할 수 있습니다가.
이게 도움이 되길 바란다.
실행 가능한 이미지의 경로 이름을 얻는 더 쉬운 방법 :
ps는 프로세스 ID가있는 경우 실행 파일의 경로를 제공 할 수 있습니다. 또한 ps는 POSIX 유틸리티이므로 휴대용이어야합니다.
따라서 프로세스 ID가 249297이면이 명령은 경로 이름 만 제공합니다.
ps -p 24297 -o comm --no-heading
인수 설명
-p-주어진 프로세스를 선택
-o comm-명령 이름을 표시합니다 (-o cmd는 전체 명령 줄을 선택합니다)
-제목 없음-표제 란을 표시하지 않고 출력 만 표시합니다.
AC 프로그램은 popen을 통해이를 실행할 수 있습니다.
C를 사용하는 경우 getwd 함수를 사용할 수 있습니다.
int main()
{
char buf[4096];
getwd(buf);
printf(buf);
}
실행 파일의 현재 디렉토리 인 표준 출력에 인쇄합니다.
프로그램의 절대 값 경로는 주 함수의 envp의 PWD에 있으며 C에는 getenv라는 함수가 있습니다.
참고 URL : https://stackoverflow.com/questions/1023306/finding-current- executables-path-without-proc-self-exe
'Programming' 카테고리의 다른 글
항목이 배열 / 목록에 있는지 확인 (0) | 2020.05.19 |
---|---|
C #에서 프리미티브의 ==와 Equals ()의 차이점은 무엇입니까? (0) | 2020.05.19 |
.NET에서 사용한 후 개체를 Null / Nothing으로 설정 (0) | 2020.05.18 |
자식 요소에 따라 요소에 CSS 스타일 적용 (0) | 2020.05.18 |
작업 표시 줄과 새로 도입 된 도구 모음의 차이점은 무엇입니까? (0) | 2020.05.18 |