Bash 和子 shell

Bash 和子 shell

根据我从在线文档收集的信息,以下内容应该为嵌入的命令部分生成一个子 shell {}

$ bash -c '{ sleep 10; echo "Sleeping process", $$; }  & echo $$; '
11237
 Sleeping process, 11237

然而,正如您所看到的,在这两种情况下,进程 ID 是相同的。我缺少什么?感谢您的指点。

答案1

只是{}在当前 shell 中将命令分组在一起,同时()启动一个新的子 shell。然而,你所做的是将分组的命令放入后台,这确实是一个新的过程;如果它在当前进程中,则不可能将其置于后台。恕我直言,用 strace 看到这种事情更容易:

sauer@humpy:~$ strace -f -etrace=process bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /tmp/file
execve("/bin/bash", ["bash", "-c", "{ sleep 10; echo \"Sleeping proce"...], [/* 20 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f15a90da700) = 0
clone(Process 25347 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f15a90da9d0) = 25347
[pid 25346] exit_group(0)               = ?
clone(Process 25348 attached (waiting for parent)
Process 25348 resumed (parent 25347 ready)
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f15a90da9d0) = 25348
[pid 25348] execve("/bin/sleep", ["sleep", "10"], [/* 20 vars */] <unfinished ...>
[pid 25347] wait4(-1, Process 25347 suspended
 <unfinished ...>
[pid 25348] <... execve resumed> )      = 0
[pid 25348] arch_prctl(ARCH_SET_FS, 0x7f922ad16700) = 0
[pid 25348] exit_group(0)               = ?
Process 25347 resumed
Process 25348 detached
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 25348
--- SIGCHLD (Child exited) @ 0 (0) ---
wait4(-1, 0x7fffaa432ad8, WNOHANG, NULL) = -1 ECHILD (No child processes)
exit_group(0)                           = ?
Process 25347 detached

sauer@humpy:~$ cat /tmp/file
25346
Sleeping process, 25347, 1

请注意,bash 命令启动,然后它创建一个带有clone().使用 -f 选项来 strace 意味着它还跟踪子进程,在运行睡眠时显示另一个分叉(好吧,“克隆”)。如果您关闭 -f,则在创建后台进程时您只会看到一个克隆调用:

sauer@humpy:~$ strace -etrace=clone bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /tmp/file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f2bdd5399d0) = 26394
sauer@humpy:~$ strace -etrace=process bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /dev/null
execve("/bin/bash", ["bash", "-c", "{ sleep 10; echo \"Sleeping proce"...], [/* 20 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fd01ae86700) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fd01ae869d0) = 26706
exit_group(0)                           = ?

如果您真的只想知道创建新进程的频率,您可以通过仅监视 fork 和 clone 调用来进一步简化:

sauer@humpy:~$ strace -etrace=fork,clone bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID;' > /dev/null
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f467fa769d0) = 27025

答案2

更新:

请看上面的答案。对于 {},我并不是真正创建子 shell。相反,这里的背景给了我我一直在寻找的答案。

好吧,基本上我使用了错误的子 shell“指示器”。我BASHPID这里除了使用它之外BASH_SUBSHELL,我可以看到确实正在创建一个子shell。

测试命令:

$ bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL; }  & echo $BASHPID; '
12074
 Sleeping process, 12075, 1

另一个测试命令还显示 shell 和子 shell 的父进程 ID:

$ bash -c '{ sleep 10; echo "Sleeping process", $BASHPID, $BASH_SUBSHELL, $PPID; }  & echo $BASHPID, $PPID; '
12411, 9128
$ Sleeping process, 12412, 1, 9128

相关内容