linux - 执行单个命令时 Bash 'swallowing' 子 shell 子进程

标签 linux bash shell subshell

遇到了意外的 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 等要运行。
  • 退出状态不需要反转或以其他方式修改。
  • 不需要取消任何重定向。

这可以节省内存,使进程的启动时间稍微快一些(因为它不需要 forked),并确保传送到 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/

相关文章:

android - 在 Android Studio 上运行终端命令

linux - Linux中如何在用户注销时自动运行脚本?

mysql - 为什么我的 Slave 没有在 MYSQL 上启动?

linux - 使用 bash 检查 FTP 登录数据

java - spring-boot 无法在 docker 中启动

bash - 尝试通过 docker exec 获取 bash,但没有任何效果(Jaeger 容器)--> OCI 运行时执行失败

linux - 如何防止暴露 truecrypt 参数?

linux env 命令没有按预期工作?

bash - Bash 脚本中的 SSH 搞乱文件读取

php - 长时间运行的 php 脚本挂起