On an Ubuntu 12.04, with GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu), I tried the following command:

$ bash -c 'pstree -s $$'
$ bash -c 'pstree -s $$;echo'

I think the second one is as my expectation: the first bash is where I executed these commands; the second bash is what I started with bash -c ...; and then the second bash will start a subprocess called pstree.

However, I'm wondering what happened with the first one. Why the second bash disappeared and the pstree became a subprocess of the original bash? And why the answer for the previous question doesn't apply to the second bash -c ...?

1 Answer 1


I would have said that it is simply Tail Call Optimization, but in fact (as the last link points out), bash doesn't optimize tail calls. It only seems to optimize the case where the command to be executed is a simple command (that is, not a compound command) though not when there are redirections. It also seems to optimize when the command is the last in an and-or list (as in true && true && cmd or false && true || cmd) but not when there are traps in place (as in trap uname EXIT && cmd) as it would be incorrect to do so.

The second command is not a tail call of pstree, because pstree is not the last thing in the command line. (It's a tail call of echo, but echo is usually a built-in so no subprocess will be created for it regardless.)

To save reading all those links (although I hope they are interesting), the idea is that if you know that a function/program/whatever will return immediately after calling some other function/program/whatever, and that the value returned will be the value returned by the called function/program/whatever, then you might as well just reuse the current stack frame (or process, in the case of a shell script) instead of pushing a new stack frame (creating a new process), calling the function (running the script), and then returning. In a shell script, you can do that manually by using exec for the last command, but it would be possible for the shell to do that automatically.

zsh and ksh both seem to be able to do that, but not bash:

$ zsh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
$ ksh -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'
$ bash -c 'if [[ -n foo ]]; then pstree -s $$; else echo hello; fi'

But that observation is just an observation, based on the versions of those shells which I happen to have installed, so 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)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .