为什么在分组命令中执行简单命令不会派生子shell进程,而复合命令会做到这一点

时间:2019-06-16 07:20:57

标签: linux bash shell subshell

我知道分组命令(command-list)会创建一个 subshel​​l环境,列出的每个命令都在该 subshel​​l 中执行。但是,如果我在分组命令中执行一个简单的命令(使用ps命令来输出进程),则不会输出任何子Shell进程。但是,如果我尝试在分组命令中执行命令列表(复合命令),则会输出子shell进程。为什么会产生这样的结果?

  • 在分组命令中执行简单命令(仅ps命令)的测试:
    [root@localhost ~]# (ps -f)
    
    具有以下输出:
    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)
    
    具有以下输出:
    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
    

我测试了许多其他命令(复合命令和简单命令),但是结果是相同的。我想即使我在分组命令中执行了一个简单的命令,bash也应该派生一个子shell进程,否则它将无法执行该命令。但是我为什么看不到呢?

2 个答案:

答案 0 :(得分:4)

Bash优化执行。它检测到( )组内只有一个命令,并调用fork + exec而不是fork + fork + {{1 }}。这就是为什么您在进程列表中少看到一个exec进程的原因。使用命令花费更多时间bash来消除计时时,更容易检测到。另外,您可能想阅读unix.stackexchange上的this thread

我认为优化是在execute_in_subshell()函数的( sleep 5 )内部某个地方完成的(箭头execute_cmd.c由我添加):

>

,在execute_disk_command()函数中,我们还可以阅读:

 /* 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. */

答案 1 :(得分:2)

似乎是一种优化,破折号似乎也在这样做:

运行

bash -c '( sleep 3)' & sleep 0.2 && ps #or with dash

更强大:

strace -f -e trace=clone dash -c '(/bin/sleep)' 2>&1 |grep clone # 1 clone

显示该子shell被跳过,但是如果子对象之后在该子shell中要进行后期处理,则会创建该子shell:

strace -f -e trace=clone dash -c '(/bin/sleep; echo done)' 2>&1 |grep clone #2 clones

Zsh和ksh进一步向前迈进了一步,而且(当他们看到这是脚本中的最后一条命令时):

strace -f -e trace=clone ksh -c '(/bin/sleep; echo done)' 2>&1 |grep clone # 0 clones

它们根本不分叉(=克隆),直接在shell进程中执行。