执行单个命令时,bash“吞咽"子shell子进程 [英] Bash 'swallowing' sub-shell children process when executing a single command

查看:184
本文介绍了执行单个命令时,bash“吞咽"子shell子进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

碰到了意外的bash/sh行为,我想知道有人可以解释其背后的原理,并提供以下问题的解决方案.

Bumped into an unexpected bash/sh behavior and I wonder someone can explain the rationale behind it, and provide a solution to the question below.

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

In an interactive bash shell session, I execute:

$ bash -c 'sleep 10 && echo'

在Linux上使用ps时,它看起来像这样:

With ps on Linux it looks like this:

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

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

进程树是我所期望的:

  • 我的交互式bash shell进程($)
  • 子shell进程(bash -c ...)
  • 儿童睡眠过程
  • My interactive bash shell process ($)
  • A children shell process (bash -c ...)
  • a sleep children process

但是,如果我的bash -c command 部分是单个命令,例如:

However, if the command portion of my bash -c is a single command, e.g.:

$ bash -c 'sleep 10'

然后,吞没了中间子外壳,并且我的交互式终端会话在子进程中直接"执行了睡眠. 进程树如下所示:

Then the middle sub-shell is swallowed, and my interactive terminal session executes sleep "directly" as children process. The process tree looks like this:

\_ -bash \_ sleep 10

\_ -bash \_ sleep 10

因此,从进程树的角度来看,这两个结果相同:

So from process tree perspective, these two produce the same result:

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

这是怎么回事?

现在我的问题是:是否有一种方法可以强制中间外壳,而不管传递给bash -c ...的表达式的复杂性如何?

Now to my question: is there a way to force the intermediate shell, regardless of the complexity of the expression passed to bash -c ...?

(我可以将; echo;之类的内容添加到我的实际命令中,并且该命令有效",但我宁愿不这样做.是否存在一种更适当的方法来强制中间过程存在?)

(I could append something like ; echo; to my actual command and that "works", but I'd rather not. Is there a more proper way to force the intermediate process into existence?)

(ps输出中的错别字;按照注释中的建议删除了sh标记;还有一个错别字)

(edit: typo in ps output; removed sh tag as suggested in comments; one more typo)

推荐答案

实际上有一条评论

There's actually a comment in the bash source that describes much of the rationale for this feature:

/* 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 '...'情况下,当

In the bash -c '...' case, the CMD_NO_FORK flag is set when determined by the should_suppress_fork function in builtins/evalstring.c.

让Shell执行此操作总是总是对您有利.它仅在以下情况下发生:

It is always to your benefit to let the shell do this. It only happens when:

  • 输入来自硬编码的字符串,外壳位于该字符串中的最后一条命令.
  • 命令完成后,将不再运行其他命令,陷阱,挂钩等.
  • 退出状态不需要反转或进行其他修改.
  • 无需撤消重定向.

这样可以节省内存,使进程的启动时间略短一些(因为不需要fork),并确保传递到PID的信号直接进入正在运行的进程,使得sh -c 'sleep 10'的父级可以准确地确定哪个信号杀死了sleep,如果它实际上被信号杀死了.

This saves memory, causes the startup time of the process to be slightly faster (since it doesn't need to be forked), and ensures that signals delivered to your PID go direct to the process you're running, making it possible for the parent of sh -c 'sleep 10' to determine exactly which signal killed sleep, should it in fact be killed by a signal.

但是,如果出于某种原因想要抑制它,则只需设置一个陷阱-任何陷阱都可以:

However, if for some reason you want to inhibit it, you need but set a trap -- any trap will do:

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

这篇关于执行单个命令时,bash“吞咽"子shell子进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆