Bash 'swallowing' sub-shell children process when executing a single command Bash 'swallowing' sub-shell children process when executing a single command shell shell

Bash 'swallowing' sub-shell children process when executing a single command


There's actually a comment in the bash source that describes much of the rationale for this feature:

/* If this is a simple command, tell execute_disk_command that it   might be able to get away without forking and simply exec.   This means things like ( sleep 10 ) will only cause one fork.   If we're timing the command or inverting its return value, however,   we cannot do this optimization. */if ((user_subshell || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) &&    ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&    ((tcom->flags & CMD_INVERT_RETURN) == 0))  {    tcom->flags |= CMD_NO_FORK;    if (tcom->type == cm_simple)      tcom->value.Simple->flags |= CMD_NO_FORK;  }

In the bash -c '...' case, the CMD_NO_FORK flag is set when determined by the should_suppress_fork function in builtins/evalstring.c.

It is always to your benefit to let the shell do this. It only happens when:

  • Input is from a hardcoded string, and the shell is at the last command in that string.
  • There are no further commands, traps, hooks, etc. to be run after the command is complete.
  • The exit status does not need to be inverted or otherwise modified.
  • No redirections need to be backed out.

This saves memory, causes the startup time of the process to be slightly faster (since it doesn't need to be forked), and ensures that signals delivered to your PID go direct to the process you're running, making it possible for the parent of sh -c 'sleep 10' to determine exactly which signal killed sleep, should it in fact be killed by a signal.

However, if for some reason you want to inhibit it, you need but set a trap -- any trap will do:

# run the noop command (:) at exitbash -c 'trap : EXIT; sleep 10'