沒有子shell程序的子shell範例
我最近才了解到“子shell”與“子shell程序”不同(例如參見“子shell”和“子程序”之間的確切區別是什麼?以及子shell和子程序的POSIX定義)。
為了讓自己相信這一點,我正在尋找一個命令來說明(證明)子shell 是在沒有產生子shell 的情況下創建的。
現在,我嘗試的一切似乎都會在創建子shell時產生一個子shell:
$ echo $BASHPID; (pwd; cd ..; echo $BASHPID; pwd); pwd # `( ...)` executed in a subshell # and in a child-shell process $ >&2 ps | ps # Theoretically executed in two subshells and apparently without child-shells # but I cannot be sure due to the outcome of the next example $ $ >&2 echo $BASHPID | ps # `ps` doesn't display a child-shell for the execution of `echo` 953790 # but `echo $BASHPID` shows a new process that is necessarily PID TTY TIME CMD # a child-shell since echo is a built-in 948538 pts/2 00:00:00 bash 953791 pts/2 00:00:00 ps
我正在尋找一種方法來證明擁有子外殼並不一定意味著擁有子外殼……
重擊 5.0.17
在 bash shell 中,子 shell 是通過派生一個子程序來實現的,因此您不會看到子 shell 沒有在該 shell 的子程序中執行的情況。
ksh93 是我所知道的唯一一個在子shell 可能時跳過分叉的shell(這種優化仍然有很多錯誤,並且在AT&T 解散編寫它的團隊後試圖維護它的後續人員已經考慮刪除)。
例如,如果您這樣做:
strace ksh93 -c 'pwd; (cd /; umask 0; pwd; exit 2); pwd'
您會看到 ksh93 沒有分叉任何程序,而是執行以下操作:
openat(AT_FDCWD, ".", O_RDONLY|O_PATH) = 3 fcntl(3, F_DUPFD, 10) = 10 close(3) = 0 fcntl(10, F_SETFD, FD_CLOEXEC) = 0 [...]
它將目前目錄保存在 fd 10 上。然後:
chdir("/") = 0 umask(000) = 002
這會更改子 shell 中的目前目錄和 umask。並且在子shell終止時(
exit 2
不呼叫_exit()
系統呼叫):fchdir(10) = 0 close(10) = 0
恢復目前工作目錄並:
umask(002) = 000
恢復umask。
一些像 FreeBSD 的 shell
sh
在非常特殊的情況下可以跳過分叉,比如:var=$(printf %04d "$n")
(這裡有一個
printf
內置的,並且沒有對環境進行任何更改)。在管道中,所有組件必須同時執行,因此它們必須在單獨的程序中執行,即使在 ksh93 中也是如此。
在
bash
中,它們都在子程序中執行。在 AT&T ksh 或 zsh 中,或 withbash -O lastpipe
(非互動式時),最右邊的不是(當然,您仍然需要 fork 一個子程序來執行外部命令,例如ps
)。您看不到額外的
bash
程序,ps >&2 | ps
或者(ps)
因為ps
直接在該子程序中執行,在執行之前ps
是 bash 解釋管道組件:子外殼。例如,在:n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$n"
您將在 bash 和zsh /ksh93 中看到
2
and 。並在子程序中執行,直接在之前完成的子shell程序中執行,在 bash 中對於(and ) 相同,但在/ /中,是在主 shell 程序中完成的,並且子程序僅用於執行該外部實用程序,就像您不作為管道的一部分執行時一樣。0``2``2``/bin/true``/bin/echo``/bin/true``n=1``/bin/echo``n=2``zsh``ksh``bash -O lastpipe``n=2``/bin/echo "$((n=2))"
在
bash
(與zsh
/相反ksh
)中,您確實在 中看到了一個額外的bash
過程(: anything; ps)
,僅當子shell 只有一個外部命令時才進行優化,您需要在exec
那里手動進行優化:(: anything; exec ps)
.也一樣
{ ps; } | cat
。