為什麼 `bash -c somecommand` 有時不留下 bash 進程?

為什麼 `bash -c somecommand` 有時不留下 bash 進程?

在 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 && cmdor false && true || cmd)時,它似乎也會優化,但當有traps 到位時(如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)

相關內容