Hi Matthew, I have a couple questions:
1. How are you testing this? (small concrete cases are helpful) 2. Do you have some specific builtins that exhibit that behavior? (it doesn't make sense for builtins to fork on their own, unless you're doing an explicit subshell. Like you mentioned, executing cd in a child process is moot 3. Can you send straces that show these forks? I cannot reproduce this behavior: dualbus@debian-512mb-sfo1-01:~$ PS1= strace -fe clone,execve,chdir bash -c 'builtin echo $BASH_VERSION; cd /; /bin/echo' execve("/bin/bash", ["bash", "-c", "builtin echo $BASH_VERSION; cd /"...], [/* 16 vars */]) = 0 4.3.30(1)-release chdir("/") = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa6dbec69d0) = 1490 Process 1490 attached [pid 1490] execve("/bin/echo", ["/bin/echo"], [/* 16 vars */]) = 0 [pid 1490] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1490, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++ dualbus@debian-512mb-sfo1-01:~$ strace -fe clone,execve,chdir bash -c 'builtin echo $BASH_VERSION; cd /; /bin/echo' execve("/bin/bash", ["bash", "-c", "builtin echo $BASH_VERSION; cd /"...], [/* 15 vars */]) = 0 4.3.30(1)-release chdir("/") = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0405bf29d0) = 1497 Process 1497 attached [pid 1497] execve("/bin/echo", ["/bin/echo"], [/* 16 vars */]) = 0 [pid 1497] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1497, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++ dualbus@yaqui:~$ strace -fe clone,execve,chdir bash -c 'builtin echo $BASH_VERSION; cd /; /bin/echo' execve("/bin/bash", ["bash", "-c", "builtin echo $BASH_VERSION; cd /"...], [/* 26 vars */]) = 0 4.3.46(1)-release chdir("/") = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f87132ba9d0) = 3661 strace: Process 3661 attached [pid 3661] execve("/bin/echo", ["/bin/echo"], [/* 26 vars */]) = 0 [pid 3661] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3661, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++ [dual...@ma.sdf.org ~]$ strace -fe clone,execve,chdir bash -c 'builtin echo $BASH_VERSION; cd /; /bin/echo' execve("/bin/bash", ["bash", "-c", "builtin echo $BASH_VERSION; cd /"...], [/* 42 vars */]) = 0 4.1.2(1)-release chdir("/") = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f9e205989d0) = 29707 Process 29707 attached [pid 29707] execve("/bin/echo", ["/bin/echo"], [/* 43 vars */]) = 0 [pid 29707] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=29707, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++ There are definitely some optimization strategies bash uses under some cases were it avoids forking (but this applies to external commands), like this case: dualbus@yaqui:~$ strace -fe clone,execve,chdir bash -c '/bin/echo $BASH_VERSION' execve("/bin/bash", ["bash", "-c", "/bin/echo $BASH_VERSION"], [/* 26 vars */]) = 0 execve("/bin/echo", ["/bin/echo", "4.3.46(1)-release"], [/* 25 vars */]) = 0 4.3.46(1)-release +++ exited with 0 +++ Since it's a single command in the -c ..., which means that it's safe for bash to do exec to replace the current process with the external command.