众所周知,当运行时bash -c "COMMAND"(至少在 Linux 的常见版本中),当有一个没有任何元字符(除了space、tab或newline)的命令时,bash -c进程不会分叉,而是将自身替换为COMMAND直接用系统调用执行execve优化,所以结果只会是一个进程。
$ pid=$$; bash -c "pstree -p $pid"\nbash(5285)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree(14314)\nRun Code Online (Sandbox Code Playgroud)\n如果有任何元字符(例如重定向)或多个命令(无论如何都需要一个元字符),bash则会分叉它执行的每个命令。
$ pid=$$; bash -c ":; pstree -p $pid"\nbash(5285)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash(28769)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree(28770)\n\n$ pid=$$; bash -c "pstree -p $pid 2>/dev/null"\nbash(5285)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80bash(14403)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree(14404)\nRun Code Online (Sandbox Code Playgroud)\n这是一个未记录的优化功能(这意味着它无法得到保证),还是在某处记录并得到保证?
\n注意:我认为并非所有版本的bash行为都是如此,并且在某些版本上,它只是被视为实现细节而不是保证,但我想知道是否至少有一些bash版本明确支持这一点并且记录此情况的条件。;例如,如果命令后面有一个字符,没有任何第二个命令,bash仍然execve不会分叉。
$ pid=$$; bash -c "pstree -p $pid ; "\nbash(17516)\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80pstree(17658)\nRun Code Online (Sandbox Code Playgroud)\n正如我所提到的,这种行为对于经验丰富的用户来说是众所周知的 …
考虑以下(含sh为/bin/dash):
$ strace -e trace=process sh -c 'grep "^Pid:" /proc/self/status /proc/$$/status'
execve("/bin/sh", ["sh", "-c", "grep \"^Pid:\" /proc/self/status /"...], [/* 47 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fcc8b661540) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fcc8b661810) = 24865
wait4(-1, /proc/self/status:Pid: 24865
/proc/24864/status:Pid: 24864
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 24865
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=24865, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++
Run Code Online (Sandbox Code Playgroud)
没有什么不寻常的,从主 shell 进程中grep替换了一个分叉进程(这里是通过 完成的clone())。到现在为止还挺好。
现在使用 bash 4.4: …