为什么 `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)

相关内容