2017-06-15 47 views
7

碰到一个意外的bash/sh行为,我想知道有人可以解释它背后的基本原理,并为下面的问题提供解决方案。当执行单个命令时,Bash'吞咽'子shell子进程

在交互式bash shell会话,我执行:

$ bash -c 'sleep 10 && echo'

随着ps在Linux上看起来是这样的:

\_ -bash \_ bash -c sleep 10 && echo \_ sleep 10

进程树是我所期望的:

  • 我互动的bash shell进程($
  • 一个孩子shell进程(bash -c ...
  • 睡眠的孩子处理

但是,如果我bash -c命令部分是命令,例如:

$ bash -c 'sleep 10'

然后,中间子外壳被吞噬,并且当儿童处理时,我的交互式终端会话“直接”执行睡眠。 进程树是这个样子:

\_ -bash \_ sleep 10

所以从进程树的角度来看,这两个产生相同的结果:

  • $ bash -c 'sleep 10'
  • $ sleep 10

这是怎么回事在这?

现在我的问题:有没有办法强制中间壳,无论表达式的复杂度传递给bash -c ...

(我可以追加类似; echo;到我的实际命令和“作品”,但我宁愿不有没有更合适的方式来迫使中间过程产生了。?)

(编辑:错字在ps输出;去除sh标签在评论建议;多一个错字)

+0

为什么你会*不*希望这种优化时,它的可能吗? –

+0

在用户可以传递任意命令的环境中处理子进程时,主要确保一致的行为。我不确定绕过这个优化是我的解决方案(我遇到的实际问题必须处理sudo的更改:https://stackoverflow.com/a/34376188)。但是这种行为很有趣,我想了解更多。 – Marco

+0

伟大的问题。你的意思是说你在第一个ps树的第2行用'\ _ bash -c sleep 10 && echo'代替'\ _ bash -c sleep 10 && sleep 10'吗? – codeforester

回答

4

这事实上是在评论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 functionbuiltins/evalstring.c确定。

它是总是为了您的利益,让shell执行此操作。

  • 输入是硬编码字符串,而外壳是在该字符串的最后一个命令:它只有在发生。
  • 命令完成后没有更多的命令,陷阱,挂钩等要运行。
  • 退出状态不需要反转或以其他方式修改。
  • 不需要重定向。

这样可以节省内存,使进程的启动时间要稍微快一点(因为它并不需要是fork ED),并确保发送到您的PID信号直接到你的过程运行,使得sh -c 'sleep 10'的父母可以准确确定哪个信号被杀死sleep,实际上它是否应该被信号杀死。

但是,如果由于某种原因,你想抑制它,但你必须懂得设下的陷阱 - 任何陷阱会做:

# run the noop command (:) at exit 
bash -c 'trap : EXIT; sleep 10' 
+0

我从源头上看不出来,但在我看来,Bash只会在整个'-c'命令行中只有一个命令。所以像'bash -c':; sleep 10“'这样的东西即使在开始睡眠之后仍然运行。 – ilkkachu

+0

@ilkkachu,我直观的预期是,这可能会因版本而异 - 3.2将在2014年年底实现'should_suppress_fork',f/e之前。即使是今天的情况,我也不希望它在未来的版本中仍然如此(希望能够解决优化失败的机会) - 而陷阱将总是*必须保持shell运行。 –

+0

似乎在3.2和4.4中都有相似的行为。 – ilkkachu