"ps ax"가 "#!" 없이 실행 중인 bash 스크립트 헤더를 찾을 수 없는 이유는 무엇입니까?

"ps ax"가 "#!" 없이 실행 중인 bash 스크립트 헤더를 찾을 수 없는 이유는 무엇입니까?

이 스크립트를 실행하면 종료될 때까지 실행됩니다...

# foo.sh

while true; do sleep 1; done

...다음을 사용하여 찾을 수 없습니다 ps ax.

>./foo.sh

// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3    S+     0:00 grep --color=auto foo.sh

#!...하지만 스크립트에 일반 " " 헤더를 추가하면 ...

#! /usr/bin/bash
# foo.sh

while true; do sleep 1; done

...이 스크립트는 동일한 명령을 통해 찾을 수 있습니다 ps.

>./foo.sh

// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43   S+     0:00 /usr/bin/bash ./foo.sh
21324 pts/3    S+     0:00 grep --color=auto foo.sh

왜 그럴까요?
이것은 관련 질문일 수 있습니다. 내 생각에 " #"는 단지 주석 접두사일 뿐이고, 그렇다면 " #! /usr/bin/bash" 자체는 주석에 지나지 않습니다. 하지만 #!" "가 단순한 코멘트보다 더 중요한 의미를 갖고 있나요?

답변1

이 스크립트는 현재 대화형 쉘이 있고 -line 없이 스크립트를 실행할 때 실행 bash됩니다 . 프로세스는 출력에 로 표시됩니다 .#!bashps axbash

$ cat foo.sh
# foo.sh

echo "$BASHPID"
while true; do sleep 1; done

$ ./foo.sh
55411

다른 터미널에서:

$ ps -p 55411
  PID TT  STAT       TIME COMMAND
55411 p2  SN+     0:00.07 bash

관련된:


매뉴얼의 관련 부분은 다음과 같습니다 bash.

파일이 실행 가능한 형식이 아니거나 디렉터리가 아니기 때문에 실행에 실패한 경우,쉘 스크립트라고 가정하면, 쉘 명령이 포함된 파일입니다. 서브쉘 생성그것을 실행하기 위해. 서브쉘은 자신을 다시 초기화하여 다음을 수행합니다.그 효과는 스크립트를 처리하기 위해 새로운 쉘이 호출되는 것과 같습니다., 그러나 부모가 기억하는 명령 위치(아래 "SHELL BUILTIN COMMANDS" 아래의 해시 참조)는 자식이 유지합니다.

프로그램이 로 시작하는 파일인 경우 #!첫 번째 줄의 나머지 부분은 프로그램의 인터프리터를 지정합니다. 쉘은 지정된 인터프리터를 실행합니다.기본적으로 이 실행 파일 형식을 처리하지 않는 운영 체제에서. [...]

즉, -line을 사용하지 않고 ./foo.sh명령줄에서 명령을 실행하는 것은 하위 쉘의 파일에서 명령을 실행하는 것과 동일합니다.foo.sh#!

$ ( echo "$BASHPID"; while true; do sleep 1; done )

그리고#!예를 들어 다음을 가리키는 올바른 라인은 /bin/bash다음과 같습니다.

$ /bin/bash foo.sh

답변2

쉘 스크립트가 로 시작되면 #!첫 번째 줄은 쉘에 대한 주석입니다. 그러나 처음 두 문자는 시스템의 다른 부분인 커널에 대한 의미를 갖습니다. 이 두 캐릭터의 #!이름은셰르본. Shebang이 수행하는 작업을 이해하려면 프로그램이 실행되는 방식을 이해해야 합니다.

파일에서 프로그램을 실행하려면 커널 작업이 필요합니다. 이것은 다음과 같다execve시스템 호출. 커널은 파일 권한을 확인하고, 호출 프로세스에서 현재 실행 중인 실행 파일과 관련된 리소스(메모리 등)를 해제하고, 새 실행 파일에 리소스를 할당하고, 제어권을 새 프로그램으로 이전해야 합니다. 더 이상 언급하지 마세요). 시스템 execve호출은 현재 실행 중인 프로세스의 코드를 대체합니다. 별도의 시스템 호출이 있습니다.fork새 프로세스를 만듭니다.

이를 위해서는 커널이 실행 파일 형식을 지원해야 합니다. 파일은 기계어 코드를 포함해야 하며 커널이 이해할 수 있는 방식으로 구성되어야 합니다. 쉘 스크립트에는 기계 코드가 포함되어 있지 않으므로 이 방식으로 실행할 수 없습니다.

shebang 메커니즘을 사용하면 커널이 코드 해석 작업을 다른 프로그램으로 연기할 수 있습니다. 커널이 실행 파일이 다음 으로 #!시작하는 것을 확인하는 경우 #!여기서는 논의하지 않습니다.) 커널이 파일을 실행하라는 지시를 받고 /my/script파일이 이 행으로 시작하는 것을 발견하면 #!/some/interpreter커널은 /some/interpreter이 매개변수를 사용하여 실행 됩니다 /my/script. 그런 다음 /some/interpreter이것이 /my/script실행되어야 하는 스크립트 파일인지 결정합니다.

파일에 커널이 이해할 수 있는 형식의 네이티브 코드가 포함되어 있지도 않고 셔뱅으로 시작하지도 않으면 어떻게 될까요? 그렇다면 파일은 실행 가능하지 않으며 오류 코드 (실행 가능 형식 오류) execve와 함께 시스템 호출이 실패합니다 .ENOEXEC

이것이 이야기의 끝일 수도 있지만 대부분의 쉘은 대체 기능을 구현합니다. 커널이 를 반환하면 ENOEXEC쉘은 파일의 내용을 보고 그것이 쉘 스크립트처럼 보이는지 확인합니다. 쉘이 파일이 쉘 스크립트처럼 보인다고 생각하면 자체적으로 실행됩니다. 이를 수행하는 방법에 대한 세부 사항은 셸에 따라 다릅니다. 스크립트를 추가하면 무슨 일이 일어나고 있는지 배울 수 있고 , ps $$프로세스를 관찰하면 strace -p1234 -f -eprocess더 많은 것을 배울 수 있습니다(여기서 1234는 셸의 PID입니다).

Bash에서 이 대체 메커니즘은 호출 fork을 통해 수행되지만 execve하위 bash 프로세스는 자체적으로 내부 상태를 지우고 이를 실행하기 위해 새 스크립트 파일을 엽니다. 따라서 스크립트를 실행하는 프로세스는 원래 bash 코드 이미지와 bash가 원래 호출될 때 전달된 원래 명령줄 인수를 계속 사용합니다. ATT ksh도 같은 방식으로 동작합니다.

% bash --norc
bash-4.3$ ./foo.sh 
  PID TTY      STAT   TIME COMMAND
21913 pts/2    S+     0:00 bash --norc

이와 대조적으로 Dash는 매개변수로 전달된 스크립트 경로를 사용하여 이를 호출 ENOEXEC하여 반응합니다. /bin/sh즉, 대시에서 shebangless 스크립트를 실행하면 스크립트에 #!/bin/sh.Mksh 및 zsh 동작이 있는 것처럼 동작합니다.

% dash
$ ./foo.sh
  PID TTY      STAT   TIME COMMAND
21427 pts/2    S+     0:00 /bin/sh ./foo.sh

답변3

첫 번째 경우, 스크립트는 현재 셸에서 분기된 하위 프로세스에 의해 실행됩니다.

먼저 echo $$셸의 프로세스 ID를 상위 프로세스 ID로 사용하여 셸을 실행한 다음 확인해야 합니다.

관련 정보