Learning Bash Book에서는 서브쉘이 환경 변수와 파일 설명자 등만 상속하고 내보내지 않은 변수는 상속하지 않는다고 언급합니다.
$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var
$
내가 아는 한, 쉘은 for ()
및 for 에 대해 두 개의 서브쉘을 생성합니다 ./file
. 그런데 이 ()
경우 서브쉘이 var
변수를 내보내지 않았음에도 변수를 인식하고 이 ./file
경우 인식하지 못하는 이유는 무엇입니까?
# Strace for ()
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631
strace
나는 이것이 어떻게 일어 났는지 알아 내려고 노력했고 놀랍게도 bash가 복제 시스템 호출에 대해 동일한 인수를 사용한다는 것을 발견했습니다. 즉, 이는 포크된 프로세스를 의미하며 상위 프로세스 ()
와 ./file
동일한 프로세스 주소 공간을 가져야 합니다. ()
이 경우 변수는 서브셸에 표시되지만 ./file
복제 syscall을 기반으로 한 동일한 매개변수에도 불구하고 동일한 상황이 발생하지 않습니까?
답변1
당신이나 이 책은 서브쉘을 쉘인 서브프로세스와 혼동하고 있습니다.
일부 쉘 구성은 쉘을 생성합니다.분기자식 프로세스. Linux에서 이는 fork
로그에서 관찰되는 보다 일반적인 시스템 호출의 특별한 경우입니다. 하위 프로세스는 쉘 스크립트의 일부를 실행합니다. 자식 프로세스가 호출됩니다.clone
strace
서브쉘. 가장 간단한 구성은 다음과 같습니다 command1 &
. command1
하위 쉘에서 실행하고 후속 명령은 상위 쉘에서 실행됩니다. 서브쉘을 생성하기 위한 다른 구성에는 명령 대체 $(command2)
및 파이프 command3 | command4
( 대부분의 쉘 command3
에서 서브쉘에서 실행되지만 command4
ksh 또는 zsh에서는 실행되지 않음)가 포함됩니다.
하위 셸은 상위 프로세스의 복사본이므로 동일한 환경 변수뿐만 아니라 변수( $$
원래 셸 프로세스의 프로세스 ID 포함), 함수, 별칭, 옵션 등 내부 정의도 모두 동일합니다. 서브쉘에서 코드를 실행하기 전에 bash는 변수를 BASHPID
서브프로세스의 프로세스 ID로 설정합니다.
이를 실행하면 외부 명령이 실행됩니다 ./file
. 먼저 쉘은 자식 프로세스를 포크한 다음 자식 프로세스를 포크합니다.구현하다( execve
시스템 호출을 통해) 실행 파일 ./file
. 하위 프로세스는 상위 프로세스의 프로세스 속성(환경, 현재 디렉터리 등)을 상속합니다. 애플리케이션의 내부 측면은 호출 전반에 걸쳐 손실됩니다 execve
. 내보내지 않은 변수, 함수 등은 커널에 알려지지 않은 bash 개념이며 bash가 다른 프로그램을 실행할 때 손실됩니다. 다른 프로그램이 bash 스크립트인 경우에도 새로운 bash 인스턴스에 의해 실행될 것입니다. 이는 상위 프로세스도 bash 인스턴스라는 사실을 모르거나 신경 쓰지 않습니다. 따라서 쉘 변수(내보내지 않은 변수)는 살아남을 수 없습니다 execve
.
답변2
"Learning Orgy"라는 책이 잘못되었습니다. 서브쉘은 모든 변수를 상속합니다. $$
원래 쉘의 PID 도 보존됩니다. 그 이유는 서브셸의 경우 셸이 분기만 하고 새 셸을 실행하지 않기 때문입니다(대신 을 입력하면 ./file
새 셸과 같은 새 명령이 실행됩니다. strace 출력에서 execve
다음과 같은 내용을 참조하세요). 따라서 기본적으로 이는 단지 사본일 뿐입니다(문서화된 일부 차이점이 있음).
참고: 이는 bash에만 국한된 것이 아니며 모든 쉘에 해당됩니다.