Current shell is
$ echo $$
23173
Note the parent of ps is current shell
$ ( ps -o pid,ppid,cmd )
PID PPID CMD
8952 23173 ps -o pid,ppid,cmd
23173 23169 bash
But here , the parent of ps is the subshell (bash)
$ ( echo hello ; ps -o pid,ppid,cmd )
hello
PID PPID CMD
8953 23173 bash
8954 8953 ps -o pid,ppid,cmd
23173 23169 bash
Is bash doing optimizations ? How come an extra echo made the the difference and spawned a subshell in 3rd case ?
Yes, what you’re seeing is an optimization. Technically, the
(…)construct always starts a subshell, by definition. Most of the time, the subshell runs in a separate subprocess. This ensures that everything done in the subshell stays in the subshell. If bash can guarantee this isolation property, it’s free to use any implementation technique it likes.In the fragment
( ps -o pid,ppid,cmd ), it’s obvious that nothing can influence the parent shell, so there’s an optimization in bash that makes it not fork a separate process for the subshell. The fragment( echo hello ; ps -o pid,ppid,cmd )is too complex for the optimizer to recognize that no subshell is needed.If you experiment with
ksh, you’ll notice that its optimizer is more aggressive. For example, it doesn’t fork a subprocess for( echo hello ; ps -o pid,ppid,cmd )either.