스크립트 대신 셸에 "명령"을 제공하면 어떻게 되나요?

스크립트 대신 셸에 "명령"을 제공하면 어떻게 되나요?

이 질문의 변형이 여러 번 요청되었습니다(여기그리고여기, 예를 들어) 그러나 답변이 내 질문을 완전히 포착하지 못하거나 내가 아는 것보다 더 많은 것을 가정하는 것이 걱정됩니다.

예를 들어서 질문을 하겠지만 대략적으로 제가 이해하려고 하는 것은 (1)어떻게쉘은 실행 파일과 스크립트를 인식할 수 있으며, 그렇다면 (2) 인식 후 다음에 일어나는 일에 차이가 있습니까?

내 작업 디렉토리에 쉘 스크립트와 실행 파일이 있다고 가정합니다 script("실행 가능"은 바이너리 "기계 코드"를 의미하는 것으로 이해하지만 정확하지 않을 수 있습니다) exe. bash 쉘과 상호작용하고 있고 scriptbash와 tcsh가 모두 이를 실행할 수 있다고 가정해 보겠습니다 . 게다가 첫 번째 줄을 가정하면확실히Shebang으로 시작하세요 #!....

  1. script명령줄 프롬프트에 입력한다고 가정해 보겠습니다 . Bash는 이것이 실행 파일이 아닌 스크립트인지 어떻게 판단하며, 일단 판단한 후에는 무엇을 합니까? (제 생각에 대답은 "새 프로세스, 특히 새 쉘(예: 서브쉘)을 만들고, 이 경우 bash(shebang이 없기 때문에)를 만들고 그 안에 있는 스크립트에서 명령을 실행하는 것입니다."라고 생각합니다. 모르겠어요.)

  2. exe이제 명령줄 프롬프트에 입력한다고 가정해 보겠습니다 . Bash는 이것이 스크립트가 아닌 실행 파일인지 어떻게 결정합니까? 일단 결정되면 무엇을 합니까? (내 생각에 "새 프로세스를 만들고 실행 파일이 완료될 때까지 기다린다"는 대답이 나올 것 같은데 잘 모르겠습니다.)

  3. 이제 첫 번째 행에 script포함되도록 수정했다고 가정해 보겠습니다 . #! /bin/tcshbash는 이것이 스크립트((1)에 대한 답변에서 shebang이 무엇을 변경합니까?)이고 실행 파일이 아니라는 것을 어떻게 결정하며, 일단 결정되면 무엇을 합니까? (내 생각에 대답은 "새 프로세스, 특히 새 쉘(즉, 서브쉘)을 만들고, 이 경우에는 tcsh(나는 shebang이기 때문에)를 만들고 그 안에 있는 스크립트에서 명령을 실행하는 것"이라고 생각합니다. 그러나 나는 그렇지 않습니다. 확신하는 .)

답변1

첫째, 명령을 실행하는 시스템이 있다.

이 쉘코드를 사용하면:

cmd and its args

쉘은 나열된 디렉토리에서 cmd별명, 함수, 내장 및 실행 파일(실행 권한이 있는 일반 파일)을 찾습니다 $PATH(현재 작업 디렉토리가 해당 디렉토리에 위치하지 않는 한 $PATH이는 나쁜 습관입니다). .

후자의 경우 execve()일반적으로 3개의 인수를 사용하여 하위 프로세스에서 시스템 호출을 호출합니다.

  1. 파일 경로( /path/to/cmd)
  2. 매개변수 목록( ["cmd", "and", "its", "args", 0])
  3. 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()됩니다 .-1ENOEXEC

이 경우 POSIX 쉘이 필요하며 이를 스크립트로 처리하기 위해 execlp()C 함수(시스템 호출이 아님) 또는 env/ ...(또는 일반적으로 POSIX 도구 상자에서 명령을 실행하는 데 사용되는 모든 항목) find -exec와 같은 것이 필요 sh합니다. 무작위로 실행되는 것을 피하기 위해 경험적 방법을 사용하는 것 같습니다 sh.

sh대부분의 쉘은 마치 shebang이 있는 것처럼 실행하여 이를 수행합니다 #! /path/to/the/standard/sh -. 일부는 POSIX 호환 sh구현이거나 하위 프로세스에서 파일 자체를 해석하여 이를 수행하는 POSIX sh 모드를 사용합니다.

따라서 세 가지 시나리오의 경우:

  1. shebang-less 대신 script실행하면 작동할 수 있습니다 . 쉘은 하위 프로세스에서 실행되고 알려진 형식이 아니기 때문에 ENOEXEC에서 실패하므로 쉘은 (선택적으로) 어떻게 보이는지 확인합니다. 내에서 실행될 수 있습니다. 구문 ( 또는 변형 ) 또는 자체적으로 해석됩니다(부모 쉘 프로세스는 종료될 때까지 기다립니다)../scriptscript/usr/bin/scriptexecve("./script", ["./script", 0], env)execve()shexecve("/bin/sh", ["sh", "-", "./script"])argv[0]-
  2. ./exe:shell은 execve("./exe", ["./exe", 0], env)자식 프로세스에서 이 작업을 수행하고 자식 프로세스는 성공하며 부모 프로세스는 해당 작업이 종료될 때까지 기다립니다.
  3. ./script#! /bin/tcshshebang: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) 함수를 #!사용하는 응용 프로그램에는 쉘 스크립트의 어떤 라인도 디스패치되지 않습니다 . 쉘에서 이 작업을 수행하거나 유사한 논리를 직접 구현해야 합니다. (즉, 쉘은 자체 검색을 구현한 다음 를 사용하고 비슷한 방식으로 오류를 복구할 수 있습니다.)pPATHexecPATHexecve

관련 정보