在 Ubuntu 12.04 上,使用 GNU bash,版本 4.2.25(1)-release (x86_64-pc-linux-gnu),我嘗試了以下命令:
$ bash -c 'pstree -s $$'
init───sshd───sshd───sshd───bash───pstree
$ bash -c 'pstree -s $$;echo'
init───sshd───sshd───sshd───bash───bash───pstree
我認為第二個是我的預期:第一個bash
是我執行這些命令的地方;第二個bash
是我開始的bash -c ...
;然後第二個 bash 將啟動一個名為 的子程序pstree
。
不過,我想知道第一個發生了什麼事。為什麼第二個bash
消失並且pstree
成為原始的子流程bash
?為什麼前一個問題的答案不適用於第二個問題bash -c ...
?
答案1
我會說這只是尾巴 稱呼 最佳化,但事實上(正如最後一個連結指出的那樣),bash
並沒有優化尾部調用。它似乎只優化了要執行的命令是簡單命令(即不是複合命令)的情況,但在存在重定向時則不然。當指令是 and-or 清單中的最後一個(如true && true && cmd
or false && true || cmd
)時,它似乎也會優化,但當有trap
s 到位時(如trap uname EXIT && cmd
),因為這樣做是不正確的。
第二個命令不是 的尾部調用pstree
,因為pstree
它不是命令列中的最後一個命令。 (它是 的尾部調用echo
,但echo
通常是內建的,因此無論如何都不會為其建立子進程。)
為了節省閱讀所有這些連結(儘管我希望它們很有趣),其想法是,如果您知道函數/程序/任何內容在調用其他函數/程序/任何內容後將立即返回,並且返回的值將被調用的函數/程式/任何東西傳回的值,那麼您不妨重複使用目前的堆疊幀(或進程,在shell 腳本的情況下),而不是推送新的堆疊幀(建立新進程),呼叫函數(運行腳本),然後返回。在 shell 腳本中,您可以使用 for 最後一個命令手動執行此操作exec
,但 shell 也可以自動執行此操作。
zsh
兩者ksh
似乎都能做到這一點,但不能bash
:
$ zsh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ ksh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───pstree
$ bash -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
init───lightdm───lightdm───init───konsole───bash───bash───pstree
但這個觀察只是一個觀察,基於我碰巧安裝的那些 shell 的版本,所以 YMMV:
$ zsh --version
zsh 5.0.2 (x86_64-pc-linux-gnu)
$ ksh --version
version sh (AT&T Research) 93u+ 2012-08-01
$ bash --version
GNU bash, version 4.2.45(1)-release (x86_64-pc-linux-gnu)