Linux Bash';吞咽';执行单个命令时的子shell子进程
遇到了一个意想不到的Linux Bash';吞咽';执行单个命令时的子shell子进程,linux,bash,shell,subshell,Linux,Bash,Shell,Subshell,遇到了一个意想不到的bash/sh行为,我想知道是否有人能解释它背后的原理,并为下面的问题提供解决方案 在交互式bashshell会话中,我执行: $bash-c“睡眠10&&echo” 对于Linux上的ps,它看起来是这样的: \\uuuz-bash \_bash-c sleep 10&&echo \_睡眠10 流程树是我所期望的: 我的交互式bash shell进程($) 子shell进程(bash-c…) 儿童睡眠过程 但是,如果我的bash-c的命令部分是单个命令,例如: $bas
bash
/sh
行为,我想知道是否有人能解释它背后的原理,并为下面的问题提供解决方案
在交互式bash
shell会话中,我执行:
$bash-c“睡眠10&&echo”
对于Linux上的ps
,它看起来是这样的:
\\uuuz-bash
\_bash-c sleep 10&&echo
\_睡眠10
流程树是我所期望的:
- 我的交互式bash shell进程(
)$
- 子shell进程(
)bash-c…
- 儿童睡眠过程
bash-c
的命令部分是单个命令,例如:
$bash-c“睡眠10”
然后吞下中间的子shell,我的交互式终端会话在子进程中“直接”执行sleep。
流程树如下所示:
\\uuuz-bash
\_睡眠10
因此,从流程树的角度来看,这两种方法产生相同的结果:
$bash-c“睡眠10”
$sleep 10
bash-c…
的表达式有多复杂
(我可以在我的实际命令中添加类似于;echo;
的内容,并且“起作用”,但我宁愿不这样做。有没有更合适的方法来强制中间过程的存在?)
(编辑:在
ps
输出中键入;删除注释中建议的sh
标记;再键入一次)实际上有一条注释描述了此功能的基本原理:
/*如果这是一个简单的命令,请告诉execute\u disk\u命令
可能不需要分叉就可以脱身,只需执行即可。
这意味着像(睡眠10)这样的事情只会导致一个分叉。
但是,如果我们对命令计时或反转其返回值,
我们不能进行这种优化*/
if((user|u subshell | user|coproc)&&(tcom->type==cm|u simple | tcom->type==cm|u subshell)&&
((tcom->标志和命令时间管道)==0)&&
((tcom->标志和命令反转返回)=0))
{
tcom->flags |=CMD_NO_FORK;
如果(tcom->类型==cm\U简单)
tcom->value.Simple->flags |=CMD_NO_FORK;
}
在bash-c'…'
案例中,当由In内置/evalstring.c
确定时,设置CMD\u NO\u FORK
标志
让shell这样做总是对您有利。只有在以下情况下才会发生:
- 输入来自硬编码字符串,shell位于该字符串中的最后一个命令
- 命令完成后,将不再运行其他命令、陷阱、挂钩等
- 退出状态不需要反转或修改
- 无需退出重定向
fork
ed),并确保发送到PID的信号直接发送到正在运行的进程,从而使sh-c“sleep 10”
的父进程能够准确地确定哪个信号被杀死sleep
,如果它真的被信号杀死了
但是,如果出于某种原因想要抑制它,您只需要设置一个陷阱——任何陷阱都可以:
# run the noop command (:) at exit
bash -c 'trap : EXIT; sleep 10'
如果可能的话,为什么不希望进行这种优化?在用户可以传递任意命令的环境中,在处理子进程时,主要确保行为一致。我不确定绕过这个优化是否是我的解决方案(我面临的实际问题必须处理sudo:的变化)。但这种行为很有趣,我想了解更多。问得好。你的意思是在你第一个ps树的第2行说
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\&echo
而不是\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\。因此类似于bash-c:;sleep 10“
即使在启动睡眠后仍能保持shell运行。@ilkkachu,我的直觉预期是,这可能会因版本而异--3.2将在2014年底实现应禁止fork
,f/e之前。即使是今天的情况,我也不希望在未来的版本中仍然如此(这有望解决丢失的优化机会)——而陷阱总是需要保持shell运行。在3.2和4.4.nod中的行为似乎类似。了解当前的行为很有用,但我仍然怀疑它是否值得信赖。如果在脚本的最后一个命令中使用隐式的exec
,可以让脚本运行得更快(当然,在典型情况下只有几毫秒,但仍然如此),那么如果有人编写它,Chet为什么会拒绝该补丁呢?谢谢。如果您想知道为什么我要强制中间shell在所有情况下都存在,那么答案与这里提到的sudo
中的更改有关:。对sudo的kill过去被转发给它的子进程,但现在已经不是这样了,所以我尝试在不同的进程组中生成我的进程。