이 질문의 변형이 여러 번 요청되었습니다(여기그리고여기, 예를 들어) 그러나 답변이 내 질문을 완전히 포착하지 못하거나 내가 아는 것보다 더 많은 것을 가정하는 것이 걱정됩니다.
예를 들어서 질문을 하겠지만 대략적으로 제가 이해하려고 하는 것은 (1)어떻게쉘은 실행 파일과 스크립트를 인식할 수 있으며, 그렇다면 (2) 인식 후 다음에 일어나는 일에 차이가 있습니까?
내 작업 디렉토리에 쉘 스크립트와 실행 파일이 있다고 가정합니다 script
("실행 가능"은 바이너리 "기계 코드"를 의미하는 것으로 이해하지만 정확하지 않을 수 있습니다) exe
. bash 쉘과 상호작용하고 있고 script
bash와 tcsh가 모두 이를 실행할 수 있다고 가정해 보겠습니다 . 게다가 첫 번째 줄을 가정하면확실히Shebang으로 시작하세요 #!...
.
script
명령줄 프롬프트에 입력한다고 가정해 보겠습니다 . Bash는 이것이 실행 파일이 아닌 스크립트인지 어떻게 판단하며, 일단 판단한 후에는 무엇을 합니까? (제 생각에 대답은 "새 프로세스, 특히 새 쉘(예: 서브쉘)을 만들고, 이 경우 bash(shebang이 없기 때문에)를 만들고 그 안에 있는 스크립트에서 명령을 실행하는 것입니다."라고 생각합니다. 모르겠어요.)exe
이제 명령줄 프롬프트에 입력한다고 가정해 보겠습니다 . Bash는 이것이 스크립트가 아닌 실행 파일인지 어떻게 결정합니까? 일단 결정되면 무엇을 합니까? (내 생각에 "새 프로세스를 만들고 실행 파일이 완료될 때까지 기다린다"는 대답이 나올 것 같은데 잘 모르겠습니다.)이제 첫 번째 행에
script
포함되도록 수정했다고 가정해 보겠습니다 .#! /bin/tcsh
bash는 이것이 스크립트((1)에 대한 답변에서 shebang이 무엇을 변경합니까?)이고 실행 파일이 아니라는 것을 어떻게 결정하며, 일단 결정되면 무엇을 합니까? (내 생각에 대답은 "새 프로세스, 특히 새 쉘(즉, 서브쉘)을 만들고, 이 경우에는 tcsh(나는 shebang이기 때문에)를 만들고 그 안에 있는 스크립트에서 명령을 실행하는 것"이라고 생각합니다. 그러나 나는 그렇지 않습니다. 확신하는 .)
답변1
첫째, 명령을 실행하는 시스템이 있다.
이 쉘코드를 사용하면:
cmd and its args
쉘은 나열된 디렉토리에서 cmd
별명, 함수, 내장 및 실행 파일(실행 권한이 있는 일반 파일)을 찾습니다 $PATH
(현재 작업 디렉토리가 해당 디렉토리에 위치하지 않는 한 $PATH
이는 나쁜 습관입니다). .
후자의 경우 execve()
일반적으로 3개의 인수를 사용하여 하위 프로세스에서 시스템 호출을 호출합니다.
- 파일 경로(
/path/to/cmd
) - 매개변수 목록(
["cmd", "and", "its", "args", 0]
) var=value
내보낸 각 변수에 대한 문자열 목록입니다.
execve()
파일 유형에 따라 파일을 처리하는 경우 :
- ELF 바이너리 실행 파일인 경우 메모리에 해당 파일(또는 그 일부)을 로드/매핑하고 더 많은 공유 라이브러리를 로드하고 실행을 시작하는 동적 링커도 가능합니다.
- 로 시작하면
#!
(셸이 아님) 줄의 나머지 부분을 실행할 다른 파일로 해석하여 파일 경로를 명령에 대한 추가 인수로 전달합니다. (만약 그렇다면#! foo bar
, 같은 일을 할 것입니다execve("foo", ["foo" or "cmd", "bar", "/path/to/cmd", "and", "its", "args"], env)
). - 시스템은 ELF 외에도 다양한 기본 실행 파일 형식을 지원할 수 있으며 일부 시스템은 인터프리터를 파일 시작 부분과 일치하는 패턴과 연결하도록 구성할 수 있습니다(Linux의 binfmt_misc 참조).
프로세스 메모리는 프로세스 중에 대부분 지워지고 execve()
프로세스가 실행 파일에서 코드를 실행하고 있기 때문에 결코 반환되지 않습니다.
파일 형식이 인식되지 않으면 오류 코드로 반환 (실패 표시) execve()
됩니다 .-1
ENOEXEC
이 경우 POSIX 쉘이 필요하며 이를 스크립트로 처리하기 위해 execlp()
C 함수(시스템 호출이 아님) 또는 env
/ ...(또는 일반적으로 POSIX 도구 상자에서 명령을 실행하는 데 사용되는 모든 항목) find -exec
와 같은 것이 필요 sh
합니다. 무작위로 실행되는 것을 피하기 위해 경험적 방법을 사용하는 것 같습니다 sh
.
sh
대부분의 쉘은 마치 shebang이 있는 것처럼 실행하여 이를 수행합니다 #! /path/to/the/standard/sh -
. 일부는 POSIX 호환 sh
구현이거나 하위 프로세스에서 파일 자체를 해석하여 이를 수행하는 POSIX sh 모드를 사용합니다.
따라서 세 가지 시나리오의 경우:
- shebang-less 대신
script
실행하면 작동할 수 있습니다 . 쉘은 하위 프로세스에서 실행되고 알려진 형식이 아니기 때문에 ENOEXEC에서 실패하므로 쉘은 (선택적으로) 어떻게 보이는지 확인합니다. 내에서 실행될 수 있습니다. 구문 ( 또는 변형 ) 또는 자체적으로 해석됩니다(부모 쉘 프로세스는 종료될 때까지 기다립니다)../script
script
/usr/bin/script
execve("./script", ["./script", 0], env)
execve()
sh
execve("/bin/sh", ["sh", "-", "./script"])
argv[0]
-
./exe
:shell은execve("./exe", ["./exe", 0], env)
자식 프로세스에서 이 작업을 수행하고 자식 프로세스는 성공하며 부모 프로세스는 해당 작업이 종료될 때까지 기다립니다../script
#! /bin/tcsh
shebang:shell을 사용하면execve("./script", ["./script", 0], env)
시스템이 이를 스크립트로 인식하므로 성공할 수도 있습니다. 매개변수를 사용하여 차례로 실행execve()
됩니다 . 상위 쉘은 평소와 같이 하위 쉘을 기다립니다./bin/tcsh
./script
답변2
쉘에 다음 명령을 실행하면
alpha beta gamma
명령줄의 모든 확장이 완료된 후 쉘은 alpha
명령 유형이 무엇인지 확인해야 합니다. 별칭이나 정의된 함수가 아닌 경우 외부 명령이어야 합니다. 이 경우 지정된 디렉터리에서 프로그램을 검색하고 PATH
, 찾으면 운영 체제(fork + exec)를 사용하여 실행합니다.
스크립트는 줄로 시작할 필요가 없습니다 #!
. 파일이 실행 가능하고 특정 유형임을 나타내는 헤더가 없으면 셸을 사용하여 처리됩니다.
GNU/Linux에서는 execve
이 파일에 대한 시스템 호출이 실패합니다. 인식할 수 있는 형식이 없습니다. 이 라이브러리는 execve
쉘을 사용하여 투명하게 다른 라이브러리를 재시도합니다. strace
무슨 일이 일어났는지에 대한 스냅샷은 다음과 같습니다 .
32056 execve("./command", ["./command"], 0xbfb71504 /* 27 vars */) = -1 ENOEXEC (Exec format error)
32056 execve("/bin/sh", ["/bin/sh", "./command"], 0xbfb71504 /* 27 vars */) = 0
다음은 한 줄만 포함하는 ./command
파일입니다 . echo foo
실행 권한이 있습니다.
추적된 프로그램은 execvp
이 함수를 단 한 번만 호출했다고 생각합니다. 이 함수는 내부적으로 execve
커널 시스템 호출을 호출합니다. 처음 호출할 때는 스크립트를 직접 실행할 수 없으므로 execvp
이번에는 /bin/sh
실행 파일을 인수 0으로 사용하여 다시 시도합니다.
이것은 실제로 Linux만의 문제가 아닙니다. Linux의 문제이기도 합니다. 이는 POSIX에서 요구되며 다음과 같습니다.
일반적인 역사적 구현은 execl(), execv(), execle() 및 execve() 함수가 실행 파일(셸 스크립트 포함)로 인식되지 않는 모든 파일에 대해 [ENOEXEC] 오류를 반환한다는 것입니다. execlp() 및 execvp() 함수는 이러한 파일을 발견하면 해당 파일이 쉘 스크립트라고 가정하고 해당 파일을 해석하는 것으로 알려진 명령 해석기를 호출합니다. POSIX에서는 이제 이것이 필요합니다.
따라서 -suffixed( -searching) 함수를 #!
사용하는 응용 프로그램에는 쉘 스크립트의 어떤 라인도 디스패치되지 않습니다 . 쉘에서 이 작업을 수행하거나 유사한 논리를 직접 구현해야 합니다. (즉, 쉘은 자체 검색을 구현한 다음 를 사용하고 비슷한 방식으로 오류를 복구할 수 있습니다.)p
PATH
exec
PATH
execve