I know that grouping commands (command-list)
creates a subshell environment , and each listed command is executed in that subshell . But if I execute a simple command in the grouping command, (use the ps
command to output the processes), then no subshell process is output . But if I tried to execute a list of commands ( compound command ) in the grouping command, then a subshell process is output . Why does it produce such a result?
ps
command) in a grouping command: [root@localhost ~]# (ps -f)
with the following output: UID PID PPID C STIME TTY TIME CMD root 1625 1623 0 13:49 pts/0 00:00:00 -bash root 1670 1625 0 15:05 pts/0 00:00:00 ps -f
[root@localhost ~]# (ps -f;cd)
with the following output: UID PID PPID C STIME TTY TIME CMD root 1625 1623 0 13:49 pts/0 00:00:00 -bash root 1671 1625 0 15:05 pts/0 00:00:00 -bash root 1672 1671 0 15:05 pts/0 00:00:00 ps -f
I tested a lot of other commands (compound commands and simple commands), but the results are the same. I guess even if I execute a simple command in a grouping command, bash
should fork a subshell process, otherwise it can't execute the command. But why can't I see it?
Bash optimizes the execution. It detects that only one command is inside the (
)
group and calls fork
+ exec
instead of fork
+ fork
+ exec
. That's why you see one bash
process less in the list of processes. It is easier to detect when using command that take more time ( sleep 5 )
to eliminate timing. Also, you may want to read this thread on unix.stackexchange.
I think the optimization is done somewhere inside execute_cmd.c
in execute_in_subshell()
function (arrows >
added by me):
/* 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. */
and in execute_disk_command()
function we can also read:
/* If we can get away without forking and there are no pipes to deal with,
don't bother to fork, just directly exec the command. */
It looks like an optimization and dash appears to be doing it too:
Running
bash -c '( sleep 3)' & sleep 0.2 && ps #or with dash
as does, more robustly:
strace -f -e trace=clone dash -c '(/bin/sleep)' 2>&1 |grep clone # 1 clone
shows that the subshell is skipped, but if there's post work to be done in the subshell after the child, the subshell is created:
strace -f -e trace=clone dash -c '(/bin/sleep; echo done)' 2>&1 |grep clone #2 clones
Zsh and ksh are taking it even one step further and for (when they see it's the last command in the script):
strace -f -e trace=clone ksh -c '(/bin/sleep; echo done)' 2>&1 |grep clone # 0 clones
they don't fork (=clone) at all, execing directly in the shell process.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.