遇到了意外的 bash
/sh
行为,我想知道有人可以解释其背后的基本原理,并提供以下问题的解决方案。
在交互式 bash
shell session 中,我执行:
$ bash -c 'sleep 10 && echo'
在 Linux 上使用 ps
看起来像这样:
\_ -bash
\_ bash -c sleep 10 && echo
\_ 睡 10
进程树是我所期望的:
- 我的交互式 bash shell 进程 (
$
) - 子 shell 进程 (
bash -c ...
) - sleep 子进程
但是,如果我的bash -c
的命令 部分是单个 命令,例如:
$ bash -c 'sleep 10'
然后中间的子 shell 被吞下,我的交互式终端 session 作为子进程“直接”执行 sleep 。 进程树如下所示:
\_ -bash
\_ 睡 10
所以从进程树的角度来看,这两个产生相同的结果:
$ bash -c 'sleep 10'
$ sleep 10
这是怎么回事?
现在我的问题是:有没有办法强制使用中间 shell,而不管传递给 bash -c ...
的表达式的复杂性?
(我可以将 ; echo;
之类的内容附加到我的实际命令中并且“有效”,但我宁愿不这样做。是否有更合适的方法来强制中间过程存在? )
(编辑:ps
输出中的错字;按照评论中的建议删除了 sh
标签;又一个错字)
最佳答案
居然有评论in the bash source描述了此功能的大部分基本原理:
/* 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;
}
在 bash -c '...'
情况下,CMD_NO_FORK
标志在由 should_suppress_fork
function 确定时设置在 builtins/evalstring.c
中。
让 shell 执行此操作总是对您有利。只有在以下情况下才会发生:
- 输入来自硬编码字符串,shell 在该字符串的最后一个命令处。
- 在命令完成后没有进一步的命令、陷阱、 Hook 等要运行。
- 退出状态不需要反转或以其他方式修改。
- 不需要取消任何重定向。
这可以节省内存,使进程的启动时间稍微快一些(因为它不需要 fork
ed),并确保传送到 PID 的信号直接进入您正在运行的进程,使 sh -c 'sleep 10'
的父进程能够准确确定哪个信号杀死了 sleep
,如果它实际上被信号。
但是,如果出于某种原因你想抑制它,你只需要设置一个陷阱——任何陷阱都可以:
# run the noop command (:) at exit
bash -c 'trap : EXIT; sleep 10'
关于linux - 执行单个命令时 Bash 'swallowing' 子 shell 子进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44576773/