왜 argv에 프로그램 이름이 포함됩니까?


106

일반적인 Unix / Linux 프로그램은 명령 행 입력을 인수 개수 ( int argc) 및 인수 벡터 ( char *argv[]) 로 받아들 입니다. 의 첫 번째 요소 argv는 프로그램 이름이며 실제 인수가 뒤에옵니다.

프로그램 이름이 실행 파일에 인수로 전달되는 이유는 무엇입니까? 자신의 이름을 사용하는 프로그램의 예가 exec있습니까?


6
mv와 cp처럼?
Archemar

9
데비안 sh에서는에 링크되어 dash있습니다. 로 전화했을 때 그들은 서로 다른 행동 sh또는dash
Motte001

21
@AlexejMagura 만약 당신이 busybox(구출 디스크와 같은) 공통적 인 것을 사용한다면 , 거의 모든 것 (cp, mv, rm, ls, ...)은 busybox에 대한 상징적 인 링크입니다.
Baard Kopperud

11
나는이 찾는거야 정말 무시하기 어려운, 그래서 그것을 말할 것이다 : 당신은 아마 "GNU"프로그램을 의미한다 ( gcc, bash, gunzip, 운영 체제 ...의 나머지의 대부분을), 리눅스는 단지 커널 때문이다.
wizzwizz4

10
@ wizzwizz4 "Typical Unix / Linux 프로그램"의 문제점은 무엇입니까? "유닉스 / 리눅스에서 실행되는 일반적인 프로그램"과 같이 읽었습니다. 특정 GNU 프로그램에 대한 제한보다 훨씬 낫습니다. Dennis Ritchie는 확실히 GNU 프로그램을 사용하지 않았습니다. BTW 허드 커널은 주요 기능이없는 GNU 프로그램의 예입니다.
rudimeier

답변:


122

우선, argv[0]반드시 프로그램 이름은 아닙니다. 호출자가 시스템 호출 에 넣는 것 argv[0]입니다 execve(예 : Stack Overflow에서이 질문 참조 ). (다른 모든 변형은 exec시스템 호출이 아니라에 대한 인터페이스 execve입니다.)

예를 들어, (사용하여 다음, 가정 execl) :

execl("/var/tmp/mybackdoor", "top", NULL);

/var/tmp/mybackdoor는 실행되지만 argv[0]로 설정되어 top있으며 이는 ps실제 또는 실제 top표시됩니다. 이에 대한 자세한 내용 은 U & L SE 에서이 답변 을 참조하십시오 .

옆이 모든 설정 : 같은 공상 파일 시스템의 출현하기 전에 /proc, argv[0]프로세스가 자신의 이름에 대해 배울 수있는 유일한 방법이었다. 무엇이 좋을까요?

  • 몇몇 프로그램은 호출 된 이름에 따라 동작을 사용자 정의합니다 (보통 BusyBox의 유틸리티 와 같은 기호 또는 하드 링크에 의해 ;이 질문에 대한 다른 답변에서 더 많은 예제가 제공됩니다).
  • 또한 syslog를 통해 기록하는 서비스, 데몬 및 기타 프로그램은 종종 이름에 로그 항목을 추가합니다. 이것이 없으면 이벤트 추적이 불가능 해집니다.

18
이러한 프로그램의 예로는 bunzip2, bzcatbzip2있는 첫번째 두 세번째 심볼릭 링크이다.
Ruslan

5
@Ruslan 흥미롭게 zcat도 심볼릭 링크가 아닙니다. 그들은 대신 쉘 스크립트를 사용 하여이 기술의 단점을 피하는 것 같습니다. 그러나 --helpgzip에 옵션을 추가 한 사람이 zcat을 유지 관리하는 것을 잊었 기 때문에 완전한 출력 을 인쇄하지 못합니다 .
rudimeier

1
내가 기억할 수있는 한, GNU 코딩 표준은 argv [0]을 사용하여 프로그램 동작을 변경하지 말 것을 권장했다 ( 현재 버전의 "일반 인터페이스 표준"섹션 ). gunzip역사적 예외입니다.

19
busybox는 또 다른 훌륭한 예입니다. 다른 명령을 호출 (308) 다른 이름으로 호출 할 수 있습니다 busybox.net/downloads/BusyBox.html#commands
Pepijn 슈미츠

2
더 많은 프로그램 argv[0]이 이름을 하드 코딩하는 대신 사용법 / 도움말 출력에 삽입합니다 . 일부는 전체, 일부는 기본 이름입니다.
스펙트럼

62

많은:

  • 배쉬에서 실행 POSIX 모드argv[0]입니다 sh. 로 argv[0]시작할 때 로그인 쉘로 실행됩니다 -.
  • 로 실행하면 빔이 다르게 작동 vi, view, evim, eview, ex, vimdiff, 등
  • 이미 언급했듯이 Busybox.
  • init로 시스템이 설정된 시스템 shutdown에서 reboot, 등은 심볼릭 링크systemctl 입니다.
  • 등등.

7
또 다른 하나는 sendmailmail. 모든 단일 유닉스 MTA에는이 두 명령에 대한 심볼릭 링크가 포함되어 있으며, 호출 될 때 원본의 동작을 에뮬레이트하도록 설계되었습니다. 즉, 메일을 보내야하는 유닉스 프로그램은 어떻게해야하는지 정확하게 알고 있습니다.
Shadur

4
다른 일반적인 경우 : testand [: 전자를 호출하면 마지막 인수가 인 경우 오류를 처리합니다 ]. (실제 데비안 스 테이블에서는이 명령들이 서로 다른 두 프로그램이지만 이전 버전과 MacO는 여전히 같은 프로그램을 사용합니다). 그리고 tex, latex등등 : 이진은 동일하지만, 호출 된 방법을 찾고, 그것은 적절한 선택 구성 파일을. init유사하다.
Giacomo Catenazzi 2009 년

4
관련, [마지막 인수 인 경우 오류 생각 하지 ] .
chepner

나는 이것이 두 번째 질문에 대답하지만 첫 번째 질문에는 대답하지 않는다고 생각합니다. 나는 일부 OS 디자이너가 앉아서 의심 할 여지가 없다”고 말했다. 그렇다면 인수 배열에 이름을 포함시킬 것 같습니다.«
Joey

@Joey 네, 그 문구는 (Q : "Any there ...?": A : "Plenty : ...")
muru

34

역사적으로, argv명령 행의 "단어"에 대한 포인터 배열 일 뿐이므로 프로그램의 이름 인 첫 번째 "단어"로 시작하는 것이 좋습니다.

그리고 어떤 이름을 사용하여 호출하는지에 따라 다르게 동작하는 프로그램이 많이 있으므로 다른 링크를 만들고 다른 "명령"을 얻을 수 있습니다. 내가 생각할 수있는 가장 극단적 인 예는 busybox 인데, 호출 방식에 따라 수십 가지의 "명령"처럼 작동합니다 .

편집 : 요청에 따라 Unix 1st Edition에 대한 참조

하나는에서 예를 볼 수있는 주요 의 기능을 ccargcargv이미 사용되었다. 복사 상기 인수 parbuf내부 newarg루프의 일부 인수와 동일한 방식으로 명령 자체를 처리하는 동안. (물론, 나중에 명령의 이름 인 첫 번째 인수 만 실행합니다). 그것은 execv친 해졌고 친척은 존재하지 않았습니다.


1
이를 뒷받침하는 참조를 추가하십시오.
lesmana

빠른 감추고에서 exec실행할 명령의 이름과 최상의에서 볼 문자 포인터의 제로 종료 배열 (소요 minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s을 , 어디는 exec소요 레이블 2와 레이블 1을 참조하고 at 레이블이 2:나타나고 etc/init\0at 레이블 1:이 레이블 2에 대한 참조와 종료 0을 나타냅니다. 이는 기본적으로 execve오늘 빼기 envp입니다.
ninjalj

1
execv그리고 execl존재했다 "영원히"(즉, 1970 년대 중반에 이른 이후) - execv시스템 콜이었고, execl그것을라는 라이브러리 함수이었다.   execve그때 환경이 존재하지 않았기 때문에 존재하지 않았습니다. 가족의 다른 구성원은 나중에 추가되었습니다.
G-Man

@ G-Man execv내가 링크 한 v1 소스를 알려줄 수 있습니까? 그냥 궁금해서
dirkt

22

사용 사례:

프로그램 이름 을 사용하여 프로그램 동작을 변경할 수 있습니다 .

예를 들어 실제 바이너리에 대한 심볼릭 링크를 만들 수 있습니다.

이 기술을 사용하는 유명한 예로는 하나의 단일 바이너리와 많은 심볼릭 링크를 설치하는 busybox 프로젝트가 있습니다. (ls, cp, mv 등). 대상은 소형 임베디드 장치이기 때문에 저장 공간을 절약하기 위해 수행하고 있습니다.

이것은 setarchutil-linux 에서도 사용됩니다 :

$ ls -l /usr/bin/ | grep setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 i386 -> setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 linux32 -> setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 linux64 -> setarch
-rwxr-xr-x 1 root root       14680 2015-10-22 16:54 setarch
lrwxrwxrwx 1 root root           7 2015-11-05 02:15 x86_64 -> setarch

여기서 그들은 기본적 으로이 기술을 사용하여 많은 중복 소스 파일을 피 하거나 소스를 더 읽기 쉽게 유지합니다.

다른 사용 사례는 런타임에 일부 모듈이나 데이터를로드해야하는 프로그램입니다. 프로그램 경로가 있으면 프로그램 위치에 상대적인 경로에서 모듈을로드 할 수 있습니다 .

또한 많은 프로그램 이 프로그램 이름을 포함한 오류 메시지를 인쇄합니다 .

:

  1. POSIX 규칙 ( man 3p execve) 이기 때문에 :

argv는 새 프로그램에 전달 된 인수 문자열의 배열입니다. 일반적으로 이러한 문자열 중 첫 번째 문자열은 실행중인 파일과 관련된 파일 이름을 포함해야합니다.

  1. C 표준 (적어도 C99 및 C11)입니다.

argc의 값이 0보다 큰 경우 argv [0]이 가리키는 문자열은 프로그램 이름을 나타냅니다. 프로그램 이름을 호스트 환경에서 사용할 수없는 경우 argv [0] [0]은 널 문자입니다.

C 표준은 "파일 이름"이 아니라 "프로그램 이름"이라고 표시합니다.


3
다른 심볼릭 링크에서 심볼릭 링크에 도달해도 끊어지지 않습니까?
Mehrdad

3
@Mehrdad, 그렇습니다. 단점이며 사용자에게 혼란을 줄 수 있습니다.
rudimeier

@rudimeier : 귀하의 '왜'항목은 실제로 이유가 아니며, 단지 "호운 큐 루스"입니다. 즉, 표준이 이것을 요구하는 이유에 대한 의문을 제기합니다.
einpoklum

@einpoklum OP의 질문은 다음과 같습니다. 프로그램 이름이 실행 파일로 전달됩니까? 나는 대답했다 : POSIX와 C 표준은 우리에게 그렇게 지시합니다. 그게 진짜 이유 가 아니라고 어떻게 생각 하십니까? 내가 인용 한 문서가 존재하지 않으면 아마도 많은 프로그램이 프로그램 이름을 전달하지 못할 것입니다.
rudimeier

OP는 "POSIX 및 C 표준이 왜 그렇게 하는가?" 문구가 추상적 수준에 있음을 인정하지만 분명합니다. 현실적으로 알 수있는 유일한 방법은 발신자에게 문의하는 것입니다.
user2338816

21

호출 방식에 따라 동작을 변경하는 프로그램 외에도 프로그램 argv[0]사용법을 인쇄하는 데 유용합니다.

printf("Usage: %s [arguments]\n", argv[0]);

이로 인해 사용법 메시지가 항상 호출 된 이름을 사용합니다. 프로그램 이름이 바뀌면 사용법 메시지가 변경됩니다. 호출 된 경로 이름도 포함합니다.

# cat foo.c 
#include <stdio.h>
int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); }
# gcc -Wall -o foo foo.c
# mv foo /usr/bin 
# cd /usr/bin 
# ln -s foo bar
# foo
Usage: foo [arguments]
# bar
Usage: bar [arguments]
# ./foo
Usage: ./foo [arguments]
# /usr/bin/foo
Usage: /usr/bin/foo [arguments]

특히 사방에 살 수있는 작은 특수 목적 도구 / 스크립트에 특히 좋습니다.

이것은 GNU 도구에서도 일반적인 관행으로 보입니다 ls. 예를 들어 다음을 참조하십시오 .

% ls --qq
ls: unrecognized option '--qq'
Try 'ls --help' for more information.
% /bin/ls --qq
/bin/ls: unrecognized option '--qq'
Try '/bin/ls --help' for more information.

3
+1. 나는 같은 것을 제안하려고했다. 많은 사람들이 변화하는 행동에 집중하고 아마도 가장 명백하고 훨씬 더 널리 사용되는 것을 언급하지 않는 것이 이상합니다.
Vee

5

프로그램 타이핑을 실행합니다 : program_name0 arg1 arg2 arg3 ....

따라서 쉘은 이미 토큰을 나눠야하며 첫 번째 토큰은 이미 프로그램 이름입니다. 그리고 BTW는 프로그램 측과 쉘에 동일한 인덱스가 있습니다.

나는 이것이 초기 단계의 편의 트릭이라고 생각하고 다른 답변에서 볼 수 있듯이 매우 편리했기 때문에이 전통은 계속되어 API로 설정되었습니다.


4

기본적으로 argv에는 프로그램 이름이 포함되어 있으므로 다음과 같은 오류 메시지를 작성할 수 있습니다 prgm: file: No such file or directory.

    fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );

2

이 응용 프로그램의 또 다른 예는이 프로그램인데,이 프로그램은 그렇지 않은 것을 입력 할 때까지 자체를 ... 자체로 바꿉니다 y.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char** argv) {

  (void) argc;

  printf("arg: %s\n", argv[1]);
  int count = atoi(argv[1]);

  if ( getchar() == 'y' ) {

    ++count;

    char buf[20];
    sprintf(buf, "%d", count);

    char* newargv[3];
    newargv[0] = argv[0];
    newargv[1] = buf;
    newargv[2] = NULL;

    execve(argv[0], newargv, NULL);
  }

  return count;
}

분명히 흥미로운 종류의 예가 있지만, 이것은 자체 업데이트 바이너리와 같은 실제 용도를 가질 수 있다고 생각합니다. 예를 들어 자체 업데이트 바이너리는 자체 메모리 공간을 다운로드하거나 변경 한 새 버전으로 자체 메모리 공간을 다시 씁니다.

예:

$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n

7 | $

소스, 그리고 좀 더 많은 정보를 원하시면 .


1000에 도달 한 것을 축하합니다.
G-Man

0

프로그램의 경로는 프로그램 argv[0]이므로 설치 디렉토리에서 구성 파일 등을 검색 할 수 있습니다.
없이는 불가능 argv[0]합니다.


2
이것은 특별히 좋은 설명이 아닙니다. (char *path_to_program, char **argv, int argc)예를 들어
moopet

AFAIK, 대부분의 프로그램은 표준 위치에서 구성을 당겨 ( ~/.<program>, /etc/<program, $XDG_CONFIG_HOME) 및 중 하나를 변경하거나 바이너리 일정에 굽는 컴파일시 옵션이하는 매개 변수를 사용.
Xiong Chiamiov

0

ccache 는 컴파일러 바이너리에 대한 다른 호출을 모방하기 위해이 방식으로 작동합니다. ccache는 컴파일 캐시입니다. 요점은 동일한 소스 코드를 두 번 컴파일하지 말고 가능한 경우 캐시에서 객체 코드를 반환하는 것입니다.

로부터 ccache 맨 페이지 , "ccache를 사용하는 두 가지 방법이 있습니다. 당신은 당신의 컴파일이 ccache와 명령 앞에 수 있습니다 또는 당신은. 첫 번째 방법 (컴파일러로 명명) 심볼릭 링크 ccache하기를 만들어 ccache 컴파일러로 가장 할 수 있습니다 ccache를 사용해 보거나 특정 프로젝트에 사용하려는 경우 가장 편리합니다. 두 번째 방법은 모든 컴파일에 ccache를 사용하려는 경우 가장 유용합니다. "

symlinks 메소드는 다음 명령을 실행합니다.

cp ccache /usr/local/bin/
ln -s ccache /usr/local/bin/gcc
ln -s ccache /usr/local/bin/g++
ln -s ccache /usr/local/bin/cc
ln -s ccache /usr/local/bin/c++
... etc ...

... 그 효과는 ccache가 다른 방법으로 컴파일러에 갔을 명령을 뺏어 ccache가 캐시 된 파일을 반환하거나 명령을 실제 컴파일러에 전달할 수 있도록하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.