빈 하위 쉘의 쉘 감지

빈 하위 쉘의 쉘 감지

SC1143래핑된 쉘 명령의 주석 부분을 서브쉘로 래핑하는 것이 좋습니다.

Posix 셸은 아무 작업도 수행하지 않는 경우 하위 셸을 시작하지 않을 만큼 "충분히 똑똑"합니까? Bash와 Zsh는 어떻습니까?

답변1

~처럼이르카초 쓰다,POSIX 자체가 지정합니다

쉘은 서브쉘 환경에서 명령을 실행하여 명령 대체를 확장해야 합니다(참조:쉘 실행 환경) 및 명령 대체(텍스트)를 대체합니다.주문하다동봉되거나 "$()"백틱 처리됨)을 명령의 표준 출력으로 대체하고 교체 끝에서 하나 이상의 <newline> 문자 시퀀스를 제거합니다.

그러나 쉘의 실제 동작을 관찰하면 몇 가지 놀라운 사실이 드러납니다(글쎄, 하나). 내가 사용하는 스크립트에는 다음이 포함되어 있습니다.

echo \
Before \
`# commented` \
After

Bash와 Zsh는 주석 전용 명령 대체를 실행하기 위해 하위 쉘을 포크합니다.

$ strace -f -e process bash bttest
execve("/bin/bash", ["bash", "bttest"], 0x7ffe31522a10 /* 67 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 3134851 attached
, child_tidptr=0x7fe3ef0c6a10) = 3134851
[pid 3134851] exit_group(0)             = ?
[pid 3134851] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3134851, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 3134851
wait4(-1, 0x7ffecdd13810, WNOHANG, NULL) = -1 ECHILD (No child processes)
Before After
exit_group(0)                           = ?
+++ exited with 0 +++
$ strace -f -e process zsh bttest
execve("/usr/bin/zsh", ["zsh", "bttest"], 0x7fffa2f78140 /* 67 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 3134903 attached
, child_tidptr=0x7f236ce63750) = 3134903
[pid 3134903] exit_group(0)             = ?
[pid 3134902] kill(3134903, 0)          = 0
[pid 3134903] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3134903, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, {ru_utime={tv_sec=0, tv_usec=593}, ru_stime={tv_sec=0, tv_usec=0}, ...}) = 3134903
wait4(-1, 0x7ffcabc4a7d4, WNOHANG|WSTOPPED|WCONTINUED, 0x7ffcabc4a7f0) = -1 ECHILD (No child processes)
kill(3134903, 0)                        = -1 ESRCH (No such process)
Before After
exit_group(0)                           = ?
+++ exited with 0 +++

스프린트반면, 역따옴표 안에 있는 내용은 구문 분석되어 내장 명령을 나타내는지 외부 명령을 나타내는지 결정하고, 구문 분석 결과 빈 "노드"(빈 명령)가 생성되면,완전히 건너뛰세요:

$ strace -f -e process dash bttest
execve("/bin/dash", ["dash", "bttest"], 0x7ffe61eee5c0 /* 67 vars */) = 0
Before After
exit_group(0)                           = ?
+++ exited with 0 +++

Bash 및 Zsh와 마찬가지로 명령이 백틱으로 묶인 경우 DASH가 분기됩니다.

echo \
Before \
`: # commented` \
After

우리는 얻었다

$ strace -f -e process dash bttest
execve("/bin/dash", ["dash", "bttest"], 0x7fff762e6260 /* 41 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7effab920850) = 3905359
strace: Process 3905359 attached
[pid 3905359] exit_group(0)             = ?
[pid 3905359] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3905359, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 3905359
wait4(-1, 0x7ffdb1e8683c, WNOHANG, NULL) = -1 ECHILD (No child processes)
Before After
exit_group(0)                           = ?
+++ exited with 0 +++

따라서 최소한 하나의 쉘은 이 경우 서브쉘을 시작하지 않을 만큼 충분히 "지능적"입니다.

:ksh93과 같은 일부 셸은 테스트된 변형 에서도 strace알 수 있듯이 외부 명령을 포함하지 않는 하위 셸을 포크하지 않습니다.

$ strace -f -e process ksh93 bttest
execve("/bin/ksh93", ["ksh93", "bttest"], 0x7ffce3c73510 /* 41 vars */) = 0
Before After
exit_group(0)                           = ?
+++ exited with 0 +++

("POSIX 쉘"은 없습니다. POSIX는 참조 구현이 아니라 사양입니다. 내가 아는 한 POSIX를 엄격하게 구현하는 쉘은 없으며 오직 POSIX만 구현합니다.)

관련 정보